多线程(二)

来源:互联网 发布:如何使用广电网络电视 编辑:程序博客网 时间:2024/06/05 11:35

GCD(Grand Central Dispatch)

是苹果为多核的并行运算提出的解决方案,所以会自动合理的利用更多的cpu内核(双核,四核),它自动管理线程的声明周期(创建线程,调度线程,销毁线程)。

同步执行: 阻塞线程并等待block 中的任务执行完毕,然后当前线程才会继续往下进行

异步执行: 当前线程会继续执行下去,不会阻塞线程

串行队列: FIFO,放到串行队列里的任务,GCD会FIFO的取一个执行一个

并行队列:放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。


手动创建队列

串行队列( A dispatch queue that invokes blocks serially in FIFO order.)

dispatch_queue_t queue = dispatch_queue_create("my.test.gcd", NULL);
dispatch_queue_t queue1 = dispatch_queue_create("my.test.gcd", DISPATCH_QUEUE_SERIAL);

并行队列( A dispatch queue that may invoke blocks concurrently and supports barrier blocks submitted with the dispatch barrier API.)

dispatch_queue_t queue2 = dispatch_queue_create("my.test.gcd", DISPATCH_QUEUE_CONCURRENT);

几种串行和并行的测试代码:

1、串行同步,当前线程执行

[self methodB];
dispatch_sync(queue1, ^{
[self methodA];
});
[self methodC];
/**
2017-11-06 17:23:47.059 多线程测试[9262:313858] i am method b,thread<NSThread: 0x60800006d2c0>{number = 1, name = main}
2017-11-06 17:23:47.060 多线程测试[9262:313858] i am method a,thread<NSThread: 0x60800006d2c0>{number = 1, name = main}
2017-11-06 17:23:47.060 多线程测试[9262:313858] i am method c,thread<NSThread: 0x60800006d2c0>{number = 1, name = main}
*/

2、串行异步

[self methodB];
dispatch_async(queue1, ^{
[self methodA];
});
[self methodC];
/**
2017-11-06 17:32:21.253 多线程测试[9376:319796] i am method b,thread<NSThread: 0x60000007bf40>{number = 1, name = main}
2017-11-06 17:32:21.253 多线程测试[9376:319796] i am method c,thread<NSThread: 0x60000007bf40>{number = 1, name = main}
2017-11-06 17:32:21.262 多线程测试[9376:319796] i am method a,thread<NSThread: 0x60000007bf40>{number = 1, name = main}
*/

3、并行同步

     [self methodB];    dispatch_sync(queue2, ^{        [self methodA];    });    [self methodC];     /** 2017-11-06 17:35:47.445 多线程测试[9413:321898] i am method b,thread<NSThread: 0x60000006c6c0>{number = 1, name = main} 2017-11-06 17:35:47.445 多线程测试[9413:321898] i am method a,thread<NSThread: 0x60000006c6c0>{number = 1, name = main} 2017-11-06 17:35:47.446 多线程测试[9413:321898] i am method c,thread<NSThread: 0x60000006c6c0>{number = 1, name = main} */

4、并行异步

    [self methodB];    dispatch_async(queue2, ^{        [self methodA];    });    [self methodC];/** 2017-11-06 17:38:24.428 多线程测试[9469:324239] i am method b,thread<NSThread: 0x600000066940>{number = 1, name = main} 2017-11-06 17:38:24.429 多线程测试[9469:324239] i am method c,thread<NSThread: 0x600000066940>{number = 1, name = main} 2017-11-06 17:38:24.429 多线程测试[9469:324273] i am method a,thread<NSThread: 0x6000000714c0>{number = 3, name = (null)} block 内可能在第二次打印也可能在第三次打印 */
队列 同步 异步 串行 当前线程,一个一个的执行 其它线程,一个一个的执行 并行 当前线程,一个一个的执行 多个线程,同时执行

+
同步死锁问题,原因:因为是主线程是串行队列,执行到dispatch_sync时,向主队列插入dispatch_sync,sync等到后面的block 执行完成后才返回,fifo,主线程等待sync完成。

NSLog(@"test1");//可以打印dispatch_sync(dispatch_get_main_queue(), ^{    //崩溃    NSLog(@"test dead-lock");});NSLog(@"test2"); //不会打印//解决dispatch_async(queue2, ^{    NSLog(@"test1%@",[NSThread currentThread]);    dispatch_sync(dispatch_get_main_queue(), ^{        //崩溃        NSLog(@"test dead-lock%@",[NSThread currentThread]);    });    NSLog(@"test2");    /**     2017-11-06 17:20:23.488 多线程测试[9208:311507] test1<NSThread: 0x608000260840>{number = 3, name = (null)}     2017-11-06 17:20:23.495 多线程测试[9208:311463] test dead-lock<NSThread: 0x6000000782c0>{number = 1, name = main}     2017-11-06 17:20:23.496 多线程测试[9208:311507] test2     */});

下面这个依然死锁

[self methodB];dispatch_async(queue, ^{    [self methodA];    dispatch_sync(queue, ^{        //崩溃鸟        NSLog(@"lock-dead");    });    NSLog(@"hehe");});[self methodC];

/**
2017-11-06 18:01:01.119 多线程测试[9657:335043] i am method b,thread<NSThread: 0x600000063e00>{number = 1, name = main}
2017-11-06 18:01:01.120 多线程测试[9657:335043] i am method c,thread<NSThread: 0x600000063e00>{number = 1, name = main}
2017-11-06 18:01:01.120 多线程测试[9657:335145] i am method a,thread<NSThread: 0x60800006de80>{number = 3, name = (null)}
*/

系统创建队列

全局队列 系统设置的一个异步队列,并且无法对其进行停止,恢复等操作

dispatch_queue_t globeQueque = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

测试代码

全局队列存在优先级(不准确,不确定是否因为多核处理器速度不同引起的)

/**
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
*/

`dispatch_async(dispatch_get_global_queue(INT16_MIN, 0), ^{          for (int i = 0; i < 10; i++) { NSLog(@"background%@",[NSThread currentThread]);   }  });dispatch_async(dispatch_get_global_queue(0, 0), ^{   for (int i = 0; i < 10; i++) {           NSLog(@"default%@",[NSThread currentThread]);  }});dispatch_async(dispatch_get_global_queue(2, 0), ^{  for (int i = 0; i < 10; i++) {          NSLog(@"hight%@",[NSThread currentThread]);   }});dispatch_async(dispatch_get_global_queue(-2, 0), ^{    for (int i = 0; i < 10; i++) {           NSLog(@"low%@",[NSThread currentThread]);   }});  /** 2017-11-07 11:55:15.599 多线程测试[7297:156473] hight<NSThread: 0x60800026b140>{number = 5, name = (null)} 2017-11-07 11:55:15.599 多线程测试[7297:156462] default<NSThread: 0x608000268480>{number = 4, name = (null)} 2017-11-07 11:55:24.236 多线程测试[7297:156477] low<NSThread: 0x600000262e00>{number = 6, name = (null)} 2017-11-07 11:55:24.237 多线程测试[7297:156148] background<NSThread: 0x608000262d40>{number = 3, name = (null)} */`

给自定义队列创建优先级

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    dispatch_queue_t sQueue = dispatch_queue_create("test.queue", NULL);    dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,  0);    dispatch_set_target_queue(sQueue, gQueue);    dispatch_async(sQueue, ^{        NSLog(@"我优先级比较低");    });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        NSLog(@"我优先级高,我先执行");    });});

dispatch_after ,如果主线程延迟两秒,时间可能不准确,因为主线程runloop 延迟(以后提到runloop的时候再说这个问题)

 NSLog(@"延迟前的数据");dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{    NSLog(@"延迟后的数据");});/** 2017-11-07 12:55:12.747 多线程测试[8514:193730] 延迟前的数据 2017-11-07 12:55:14.747 多线程测试[8514:193730] 延迟后的数据 */

系统设置的默认的同步主队列
dispatch_get_main_queue() //常用于异步线程回到主线程

测试代码

    NSLog(@"test1%@",[NSThread currentThread]);  dispatch_async(globeQueque, ^{      NSLog(@"test2%@",[NSThread currentThread]);      dispatch_async(dispatch_get_main_queue(), ^{           NSLog(@"test3%@",[NSThread currentThread]);      });      /**       2017-11-07 09:31:10.491 多线程测试[1203:27491] test1<NSThread: 0x60000006bac0>{number = 1, name = main}       2017-11-07 09:31:10.491 多线程测试[1203:27529] test2<NSThread: 0x60800006d2c0>{number = 3, name = (null)}       2017-11-07 09:31:10.497 多线程测试[1203:27491] test3<NSThread: 0x60000006bac0>{number = 1, name = main}        */  });  

队列组

@abstract* A group of blocks submitted to queues for asynchronous invocation.* @abstract* Schedule a block to be submitted to a queue when all the blocks associated* with a group have completed.** @discussion* This function schedules a notification block to be submitted to the specified* queue once all blocks associated with the dispatch group have completed.** If no blocks are associated with the dispatch group (i.e. the group is empty)* then the notification block will be submitted immediately.** The group will be empty at the time the notification block is submitted to* the target queue. The group may either be released with dispatch_release()* or reused for additional operations.    * See dispatch_group_async() for more information.

对于异步并发队列我们无法监测到每一个任务什么时候执行完毕
所以我们可以放到group组,利用 dispatch_group_notify去监测组内的任务什么时候完成

dispatch_group_t group = dispatch_group_create();dispatch_queue_t globequeue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_notify(group,dispatch_get_main_queue(), ^{    NSLog(@"完成%@",[NSThread currentThread]);});//只有异步并发队列组dispatch_group_async(group, globequeue, ^{      for (int i = 0; i < 10; i++) {            NSLog(@"i---%@",[NSThread currentThread]);        }});  dispatch_group_async(group, globequeue, ^{   for (int i = 0; i < 10; i++) {       NSLog(@"i---%@",[NSThread currentThread]);   }   });  NSLog(@"等待时长%ld",dispatch_group_wait(group, DISPATCH_TIME_NOW));       /**     2017-11-06 19:18:49.707 多线程测试[14213:386987] i--->0,<NSThread: 0x6080002667c0>{number = 3, name = (null)}     2017-11-06 19:18:49.709 多线程测试[14213:386987] i--->1,<NSThread: 0x6080002667c0>{number = 3, name = (null)}     2017-11-06 19:18:49.711 多线程测试[14213:386987] i--->2,<NSThread: 0x6080002667c0>{number = 3, name = (null)}     */});

队列barrier

* The target dispatch queue to which the block is submitted.* The system will hold a reference on the target queue until the block* has finished.dispatch_queue_t barqueue = dispatch_queue_create("barrier queue", DISPATCH_QUEUE_CONCURRENT);

此方法作用是在并发队列中,完成它之前提交到队列的任务后打断,单独执行其block,并执行完成之后才能继续执行在他之后提交的任务
阻断串行队列并没什么意义,本身串行队本身就是按照顺序执行的

dispatch_async(barqueue, ^{    NSLog(@"0%@",[NSThread currentThread]);});dispatch_async(barqueue, ^{    NSLog(@"1%@",[NSThread currentThread]);});dispatch_async(barqueue, ^{    NSLog(@"2%@",[NSThread currentThread]);});dispatch_barrier_async(barqueue, ^{    sleep(1);    NSLog(@"barrier%@",[NSThread currentThread]);});dispatch_async(barqueue, ^{    NSLog(@"3%@",[NSThread currentThread]);});NSLog(@"当前线程");/** 2017-11-07 14:56:40.227 多线程测试[10216:262488] 0<NSThread: 0x60000006c400>{number = 3, name = (null)} 2017-11-07 14:56:40.227 多线程测试[10216:262486] 2<NSThread: 0x60000006c5c0>{number = 5, name = (null)} 2017-11-07 14:56:40.227 多线程测试[10216:262485] 1<NSThread: 0x608000075440>{number = 4, name = (null)} 2017-11-07 14:56:41.301 多线程测试[10216:262485] barrier<NSThread: 0x608000075440>{number = 4, name = (null)} 2017-11-07 14:56:41.301 多线程测试[10216:262485] 3<NSThread: 0x608000075440>{number = 4, name = (null)}   打印2之后中断了,并且睡了一秒才执行,然后执行后面的,相当于是线程锁,在多个线程同时操作一个对象时,读可以放在并发进行,当写的时候,我们就可以用该方法进行 */

同步阻塞

dispatch_async(barqueue, ^{    NSLog(@"0%@",[NSThread currentThread]);});dispatch_async(barqueue, ^{    NSLog(@"1%@",[NSThread currentThread]);});dispatch_async(barqueue, ^{    NSLog(@"2%@",[NSThread currentThread]);}); dispatch_barrier_sync(barqueue, ^{     sleep(1);     NSLog(@"barrier%@",[NSThread currentThread]); });NSLog(@"当前线程");dispatch_async(barqueue, ^{    NSLog(@"3%@",[NSThread currentThread]);});/** 2017-11-07 15:13:36.521 多线程测试[10417:273041] 1<NSThread: 0x608000261740>{number = 4, name = (null)} 2017-11-07 15:13:36.521 多线程测试[10417:273044] 0<NSThread: 0x600000264fc0>{number = 3, name = (null)} 2017-11-07 15:13:36.521 多线程测试[10417:273042] 2<NSThread: 0x6000002654c0>{number = 5, name = (null)} 2017-11-07 15:13:37.594 多线程测试[10417:272927] barrier<NSThread: 0x608000077080>{number = 1, name = main} 2017-11-07 15:13:37.595 多线程测试[10417:272927] 当前线程 2017-11-07 15:13:37.595 多线程测试[10417:273042] 3<NSThread: 0x6000002654c0>{number = 5, name = (null)} */除了阻塞前面的线程,还阻塞当前线程

dispatch_apply用于无序查找,遍历完之前会阻塞当前线程

NSArray * arrary = [[NSArray alloc]initWithObjects:@"2",@"1",@"4",@"6",@"7",@"9",@"8",@"0",@"3", nil];dispatch_queue_t gQueue = dispatch_queue_create("test.apply", DISPATCH_QUEUE_CONCURRENT);dispatch_apply(arrary.count, gQueue, ^(size_t index) {    NSLog(@"%zu= %@,thread%@",index,[arrary objectAtIndex:index],[NSThread currentThread]);    if ([[arrary objectAtIndex:index]  isEqual: @"6"]) {        //    }});

dispatch_resume,dispatch_suspend 线程恢复,线程停止

dispatch_queue_t suspendQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //使用在这个队列是无效的dispatch_queue_t suspendQueue = dispatch_queue_create("supsend.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(suspendQueue, ^{    for (int i = 0; i < 100; i++) {        NSLog(@"i-->%d",i);        if (i == 50) {            NSLog(@"我要停止了");            dispatch_suspend(suspendQueue);            sleep(2);             NSLog(@"我要恢复了");            dispatch_resume(suspendQueue);        }    }});/** 2017-11-07 15:39:37.614 多线程测试[10697:287713] i-->50 2017-11-07 15:39:37.614 多线程测试[10697:287713] 我要停止了 2017-11-07 15:39:39.688 多线程测试[10697:287713] 我要恢复了 2017-11-07 15:39:39.688 多线程测试[10697:287713] i-->51 */

单例(过)

`static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

});`

semaphore 信号量,当两个线程需要协调某个特定事件的完成时,对该值传递0是有用的。传递大于0的值对于管理有限的资源池是有用的,池的大小等于这个值。传递一个小于0的值将返回NULL

这里传递了一个初始值为10的信号量dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);for (int i = 0; i < 100; i ++) {   Wait (decrement) for a semaphore.    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信号量不为0,开辟10个线程去执行,信号量减小为0,阻塞队列    dispatch_async(queue, ^{        NSLog(@"%i,thread%@",i,[NSThread currentThread]);        //然后执行睡两秒        sleep(2);        //Signal (increment) a semaphore.        //然后信号量增加        dispatch_semaphore_signal(semaphore);    });}//demo 2:初始化为0的信号量dispatch_semaphore_t semphore = dispatch_semaphore_create(0);dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSLog(@"执行了block%@",[NSThread currentThread]);    sleep(10);    dispatch_semaphore_signal(semphore);});NSLog(@"这是等待前的打印%@",[NSThread currentThread]);dispatch_semaphore_wait(semphore, DISPATCH_TIME_FOREVER);NSLog(@"这是等待后的打印"); //可以看出这也是阻塞线程的一种方法 dispatch_semaphore_create(0)方法介绍一个long类型的参数, 返回一个dispatch_semaphore_t类型的信号量且值为0. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER)方法检测signal信号量的值, 若值为0, 则该方法会阻塞当前线程并等待. 第二个参数即为等待时间. 等待期间, 信号量不为0, 则该方法所在的线程获取了信号量, 继续执行. 因此, 如果使用dispatch_semaphore_wait(signal, dispatch_time(DISPATCH_TIME_NOW, (Int64)(NSEC_PER_SEC * 2)))则会等待2s时间, 超时则信号量失效, 释放对线程的阻塞. dispatch_semaphore_signal(signal)使信号量signal加1. */

总结:1、GCD偏重于对队列的管理
2、同步线程虽然容易,但是注易出现死锁问题,常用异步处理,但是异步经常需要等待多线程处理完成获取结果,更新UI,这时候可以使用队列组通知,或barrier,semaphore等方法阻塞队列,等待结果完成