一:GCD的队列简介
GCD的Queue有三大队列类型:主队列(main)、全局队列(global)、用户队列(create)
(1)主队列:
dispatch_queue_t queue =dispatch_get_main_queue();
获得主线程的dispatch队列,是一个串行队列。无法控制主线程dispatch队列的执行继续或中断。
注意:主队列在使用时不可把dispatch_sync(串行)函数添加到主队列中,否则会造成死锁。例如:
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"blala");
});
关于死锁的知识后面会详细讲解
(2)全局队列:
注意第一个参数表示的是优先级
NSQualityOfServiceUserInteractive
与用户交互的任务,这些任务通常跟UI级别的刷新相关,比如动画,这些任务需要在一瞬间完成
NSQualityOfServiceUserInitiated
由用户发起的并且需要立即得到结果的任务,比如滑动scroll view时去加载数据用于后续cell的显示,这些任务通常跟后续的用户交互相关,在几秒或者更短的时间内完成
NSQualityOfServiceUtility
一些可能需要花点时间的任务,这些任务不需要马上返回结果,比如下载的任务,这些任务可能花费几秒或者几分钟的时间
NSQualityOfServiceBackground
这些任务对用户不可见,比如后台进行备份的操作,这些任务可能需要较长的时间,几分钟甚至几个小时
NSQualityOfServiceDefault
优先级介于user-initiated 和 utility,当没有 QoS信息时默认使用,开发者不应该使用这个值来设置自己的任务
,只能是在调用顺序的概率上有大小之分,并不是一定就有先后顺序,结合上面的表来看
例如可以如下设置:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- (void)GCDDemo2
{
// 将任务添加到队列
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED,0), ^{
NSLog(@"1号位%@",[NSThread currentThread]);
}); dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE,0), ^{
NSLog(@"2号位%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT,0), ^{
NSLog(@"3号位%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED,0), ^{
NSLog(@"4号位%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"5号位%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND,0), ^{
NSLog(@"6号位%@",[NSThread currentThread]);
});
}
其调用之后的打印结果如下
第一次运行时
2016-12-18 21:56:41.920 04-GCD的初体验[6272:323079] 2号位<NSThread: 0x7fae714b5f50>{number = 3, name = (null)}
2016-12-18 21:56:41.919 04-GCD的初体验[6272:323078] 1号位<NSThread: 0x7fae71702d10>{number = 2, name = (null)}
2016-12-18 21:56:41.920 04-GCD的初体验[6272:323081] 3号位<NSThread: 0x7fae7141d5b0>{number = 4, name = (null)}
2016-12-18 21:56:41.920 04-GCD的初体验[6272:323080] 4号位<NSThread: 0x7fae71405750>{number = 5, name = (null)}
2016-12-18 21:56:41.920 04-GCD的初体验[6272:323085] 5号位<NSThread: 0x7fae7150bd60>{number = 6, name = (null)}
2016-12-18 21:56:41.922 04-GCD的初体验[6272:323082] 6号位<NSThread: 0x7fae71704a70>{number = 7, name = (null)}
第二次运行时
2016-12-18 21:58:07.447 04-GCD的初体验[6272:323959] 1号位<NSThread: 0x7fae714c1b10>{number = 13, name = (null)}
2016-12-18 21:58:07.447 04-GCD的初体验[6272:323960] 2号位<NSThread: 0x7fae716041c0>{number = 14, name = (null)}
2016-12-18 21:58:07.447 04-GCD的初体验[6272:323957] 4号位<NSThread: 0x7fae71704a70>{number = 16, name = (null)}
2016-12-18 21:58:07.447 04-GCD的初体验[6272:323961] 3号位<NSThread: 0x7fae714c15d0>{number = 15, name = (null)}
2016-12-18 21:58:07.447 04-GCD的初体验[6272:323959] 5号位<NSThread: 0x7fae714c1b10>{number = 13, name = (null)}
2016-12-18 21:58:07.450 04-GCD的初体验[6272:323962] 6号位<NSThread: 0x7fae716012c0>{number = 17, name = (null)}
(3)用户队列:
dispatch_queue_t queue =dispatch_queue_create("searialQueue",DISPATCH_QUEUE_SERIAL);//串行队列
dispatch_queue_t queue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);//并发队列
串行队列是单线程的,不会额外再多开线程
- (void)GCDDemo2
{
NSLog(@"我点了");
// 获取主队列
// dispatch_queue_t queue = dispatch_get_main_queue();
// 并发队列的创建
// dispatch_queue_t queue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);
// 串行队列的创建
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
}
}
打印结果如下
2016-12-18 22:09:14.924 08-主队列和异步同步任务[6408:330652] 我点了
2016-12-18 22:09:14.927 08-主队列和异步同步任务[6408:330682] I = 0,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.929 08-主队列和异步同步任务[6408:330682] I = 1,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.929 08-主队列和异步同步任务[6408:330682] I = 2,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.929 08-主队列和异步同步任务[6408:330682] I = 3,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.929 08-主队列和异步同步任务[6408:330682] I = 4,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.930 08-主队列和异步同步任务[6408:330682] I = 5,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.930 08-主队列和异步同步任务[6408:330682] I = 6,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.930 08-主队列和异步同步任务[6408:330682] I = 7,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.958 08-主队列和异步同步任务[6408:330682] I = 8,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
2016-12-18 22:09:14.958 08-主队列和异步同步任务[6408:330682] I = 9,当前线程为 <NSThread: 0x7fe9d1519e70>{number = 2, name = (null)}
并发队列是多线程并行,会开多个线程
- (void)GCDDemo2
{
NSLog(@"我点了");
// 获取主队列
// dispatch_queue_t queue = dispatch_get_main_queue();
// 串行队列的创建
// dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建
dispatch_queue_t queue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"I = %d,当前线程为 %@",i,[NSThread currentThread]);
});
}
}
打印结果如下
2016-12-18 22:12:35.024 08-主队列和异步同步任务[6425:332369] 我点了
2016-12-18 22:12:35.025 08-主队列和异步同步任务[6425:332755] I = 0,当前线程为 <NSThread: 0x7fcbc3707d80>{number = 2, name = (null)}
2016-12-18 22:12:35.026 08-主队列和异步同步任务[6425:332762] I = 2,当前线程为 <NSThread: 0x7fcbc352acf0>{number = 4, name = (null)}
2016-12-18 22:12:35.026 08-主队列和异步同步任务[6425:332760] I = 1,当前线程为 <NSThread: 0x7fcbc370b880>{number = 3, name = (null)}
2016-12-18 22:12:35.027 08-主队列和异步同步任务[6425:332755] I = 3,当前线程为 <NSThread: 0x7fcbc3707d80>{number = 2, name = (null)}
2016-12-18 22:12:35.028 08-主队列和异步同步任务[6425:332763] I = 4,当前线程为 <NSThread: 0x7fcbc36080e0>{number = 5, name = (null)}
2016-12-18 22:12:35.028 08-主队列和异步同步任务[6425:332762] I = 5,当前线程为 <NSThread: 0x7fcbc352acf0>{number = 4, name = (null)}
2016-12-18 22:12:35.029 08-主队列和异步同步任务[6425:332766] I = 6,当前线程为 <NSThread: 0x7fcbc3605430>{number = 6, name = (null)}
2016-12-18 22:12:35.029 08-主队列和异步同步任务[6425:332760] I = 7,当前线程为 <NSThread: 0x7fcbc370b880>{number = 3, name = (null)}
2016-12-18 22:12:35.029 08-主队列和异步同步任务[6425:332767] I = 8,当前线程为 <NSThread: 0x7fcbc3709a60>{number = 7, name = (null)}
2016-12-18 22:12:35.029 08-主队列和异步同步任务[6425:332755] I = 9,当前线程为 <NSThread: 0x7fcbc3707d80>{number = 2, name = (null)}
二:队列优先级的设定
dispatch_queue_create创建队列的优先级跟global dispatch queue的默认优先级一样,假如我们需要设置队列的优先级,可以通过dispatch_queue_attr_make_with_qos_class或者dispatch_set_target_queue方法;
//指定队列的QoS类别为QOS_CLASS_UTILITY
dispatch_queue_attr_t queue_attr = dispatch_queue_attr_make_with_qos_class (DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY,-1);
dispatch_queue_t queue = dispatch_queue_create("queue", queue_attr);
dispatch_set_target_queue的第一个参数为要设置优先级的queue,第二个参数是对应的优先级参照物
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.MyQueue",NULL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
//serialQueue现在的优先级跟globalQueue的优先级一样
dispatch_set_target_queue(serialQueue, globalQueue);
dispatch_set_target_queue的使用
dispatch_set_target_queue除了能用来设置队列的优先级之外,还能够创建队列的层次体系,当我们想让不同队列中的任务同步的执行时,我们可以创建一个串行队列,然后将这些队列的target指向新创建的队列即可,比如
dispatch_queue_t targetQueue = dispatch_queue_create("target_queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"do job1,%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.f];
});
dispatch_async(queue2, ^{
NSLog(@"do job2 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.f];
});
dispatch_async(queue2, ^{
NSLog(@"do job3 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.f];
});
可以看到执行的结果如下,这些队列会同步的执行任务
2016-12-19 00:08:29.372 09-全局并发队列[7035:379438] do job1,<NSThread: 0x7f7fcb7181c0>{number = 2, name = (null)}
2016-12-19 00:08:32.374 09-全局并发队列[7035:379438] do job2 <NSThread: 0x7f7fcb7181c0>{number = 2, name = (null)}
2016-12-19 00:08:34.380 09-全局并发队列[7035:379438] do job3 <NSThread: 0x7f7fcb7181c0>{number = 2, name = (null)}
不使用dispatch_set_target_queue,即注释掉前面设置的依赖关系之后,我们看运行结果
dispatch_queue_t targetQueue = dispatch_queue_create("target_queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
// dispatch_set_target_queue(queue1, targetQueue);
// dispatch_set_target_queue(queue2, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"do job1,%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.f];
});
dispatch_async(queue2, ^{
NSLog(@"do job2 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.f];
});
dispatch_async(queue2, ^{
NSLog(@"do job3 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.f];
});
运行结果如下:
2017-02-10 15:40:12.964 GCDDemo[1233:70659] do job2 <NSThread: 0x60800006dd80>
{number = 4, name = (null)}
2017-02-10 15:40:12.964 GCDDemo[1233:70661] do job3 <NSThread: 0x60000006edc0>
{number = 5, name = (null)}
2017-02-10 15:40:12.964 GCDDemo[1233:70658] do job1,<NSThread: 0x60800006dd40>
{number = 3, name = (null)}
dispatch_barrier_async的使用
dispatch_barrier_async用于等待前面的任务执行完毕后自己才执行,而它后面的任务需等待它完成之后才执行。一个典型的例子就是数据的读写,通常为了防止文件读写导致冲突,我们会创建一个串行的队列,所有的文件操作都是通过这个队列来执行,比如FMDB,这样就可以避免读写冲突。不过其实这样效率是有提升的空间的,当没有更新数据时,读操作其实是可以并行进行的,而写操作需要串行的执行,如何实现呢:
dispatch_queue_t queue = dispatch_queue_create("Database_Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"reading data1,currentThread = %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"reading data2,currentThread = %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"reading data3,currentThread = %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"writing data1,currentThread = %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
dispatch_barrier_async(queue, ^{
NSLog(@"writing data2,currentThread = %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"reading data4,currentThread = %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"writing data3,currentThread = %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"reading data5,currentThread = %@",[NSThread currentThread]);
});
执行结果如下
GCDDemo[1310:77528] reading data2,currentThread = <NSThread: 0x60800007ee40>{number = 4, name = (null)}
GCDDemo[1310:77531] reading data3,currentThread = <NSThread: 0x60000007a240>{number = 5, name = (null)}
GCDDemo[1310:77549] reading data1,currentThread = <NSThread: 0x60000007a500>{number = 3, name = (null)}
GCDDemo[1310:77549] writing data1,currentThread = <NSThread: 0x60000007a500>{number = 3, name = (null)}
GCDDemo[1310:77549] writing data2,currentThread = <NSThread: 0x60000007a500>{number = 3, name = (null)}
GCDDemo[1310:77549] reading data4,currentThread = <NSThread: 0x60000007a500>{number = 3, name = (null)}
GCDDemo[1310:77549] writing data3,currentThread = <NSThread: 0x60000007a500>{number = 3, name = (null)}
GCDDemo[1310:77549] reading data5,currentThread = <NSThread: 0x60000007a500>{number = 3, name = (null)}
我们将写数据的操作放在dispatch_barrier_async中,这样能确保在写数据的时候会等待前面的读操作完成,而后续的读操作也会等到写操作完成后才能继续执行,提高文件读写的执行效率
FMDB 中如何防止死锁之
dispatch_queue_set_specific 、dispatch_get_specific方法的使用
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
//并发的运行一个block任务5次
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
//并发的运行一个block任务5次
dispatch_apply(5, queue, ^(size_t i) {
NSLog(@"do a job %zu times,currentThread = %@",i+1,[NSThread currentThread]);
});
NSLog(@"go on");
打印结果如下:
GCDDemo[1361:82100] do a job 1 times,currentThread = <NSThread: 0x60800007fc00>{number = 1, name = main}
GCDDemo[1361:82155] do a job 2 times,currentThread = <NSThread: 0x60800026d280>{number = 3, name = (null)}
GCDDemo[1361:82178] do a job 3 times,currentThread = <NSThread: 0x60800026ce40>{number = 4, name = (null)}
GCDDemo[1361:82100] do a job 5 times,currentThread = <NSThread: 0x60800007fc00>{number = 1, name = main}
GCDDemo[1361:82153] do a job 4 times,currentThread = <NSThread: 0x60800026c2c0>{number = 5, name = (null)}
GCDDemo[1361:82100] go on
在某些场景下使用dispatch_apply会对性能有很大的提升,比如你的代码需要以每个像素为基准来处理计算image图片。同时dispatch apply能够避免一些线程爆炸的情况发生(创建很多线程)
//危险,可能导致线程爆炸以及死锁,开的线程过多,耗时长,因为GCD没有进行统一管理,
for (int i = 0; i < 999; i++){
dispatch_async(q, ^{...});
}
dispatch_barrier_sync(q, ^{});
// 较优选择, GCD 会管理并发,开的线程是有限的,耗时短,内存消耗少
dispatch_apply(999, q, ^(size_t i){...});
不使用dispatch_apply具体实例演示如下:
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin");
NSDate *date1 = [NSDate date];
//危险,可能导致线程爆炸以及死锁,开的线程过多,耗时长,因为GCD没有进行统一管理,
for (int i = 0; i < 999; i++){
dispatch_async(queue, ^{
NSLog(@"do a job %d times,currentThread = %@",i,[NSThread currentThread]);
});
}
dispatch_barrier_sync(queue, ^{
NSDate *date2 = [NSDate date];
double time = [date2 timeIntervalSinceDate:date1];
NSLog(@"end,用时time= %f",time);
});
打印结果入下:
开启的线程总数多大67个,内存消耗23M
2017-02-10 16:21:25.038 GCDDemo[1454:92682] end,用时time= 0.418642
使用dispatch_apply具体实例演示如下:
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin");
NSDate *date1 = [NSDate date];
// // 较优选择, GCD 会管理并发,开的线程是有限的,耗时短,内存消耗少
dispatch_apply(999, queue, ^(size_t i){
NSLog(@"do a job %zu times,currentThread = %@",i,[NSThread currentThread]);
});
NSDate *date2 = [NSDate date];
double time = [date2 timeIntervalSinceDate:date1];
NSLog(@"end,用时time= %f",time);
dispatch_barrier_sync(queue, ^{
});
打印结果如下:
开启的线程总数5个,内存消耗18M
2017-02-10 16:30:18.756 GCDDemo[1506:98719] end,用时time= 0.620222
Dispatch Block:
添加到gcd队列中执行的任务是以block的形式添加的,block封装了需要执行功能,block带来的开发效率提升就不说了,gcd跟block可以说是一对好基友,能够很好的配合使用。
创建block
我们可以自己创建block并添加到queue中去执行
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
//创建block
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"do something");
});
dispatch_async(queue, block);
在创建block的时候我们也可以通过设置QoS,指定block对应的优先级,在dispatch_block_create_with_qos_class中指定QoS类别即可:
注意此处不要混淆,我们其实也可以对queue设置优先级,不过要使用这个方法,
//指定队列的QoS类别为QOS_CLASS_UTILITY
//dispatch_queue_attr_t queue_attr = dispatch_queue_attr_make_with_qos_class //(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY,-1);
//dispatch_queue_t queue = dispatch_queue_create("queue", queue_attr);
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
NSLog(@"do something with QoS");
});
dispatch_async(queue, block);
dispatch_block_wait接口的调动用
当需要等待前面的任务执行完毕时,我们可以使用dispatch_block_wait这个接口,设置等待时间DISPATCH_TIME_FOREVER会一直等待直到前面的任务完成:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"before sleep");
[NSThread sleepForTimeInterval:1];
NSLog(@"after sleep");
});
dispatch_async(queue, block);
//等待前面的任务执行完毕
dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
NSLog(@"coutinue");
打印结果如下
2017-02-11 09:56:04.714 GCDDemo[3402:261809] before sleep
2017-02-11 09:56:04.714 GCDDemo[3402:261473] coutinue
2017-02-11 09:56:14.715 GCDDemo[3402:261809] after sleep
- (void)waiteBlockDemo{
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"before sleep");
[NSThread sleepForTimeInterval:10];
NSLog(@"after sleep");
});
dispatch_async(queue, block);
//只设置等待前面的任务执行一秒钟,一秒内要是前面的block执行不完的话就会跳过去直接执行后面
dispatch_time_t time = 1.0;
dispatch_block_wait(block, time);
NSLog(@"coutinue");
}
打印结果如下
2017-02-11 09:56:45.059 GCDDemo[3416:262712] before sleep
2017-02-11 09:56:45.059 GCDDemo[3416:262642] coutinue
2017-02-11 09:56:55.061 GCDDemo[3416:262712] after sleep
dispatch_block_notify(针对block任务在没有任何关系的queue中建立依赖关系的情况)
dispatch_block_notify当观察的某个block执行结束之后立刻通知提交另一特定的block到指定的queue中执行
//该函数有三个参数,第一参数是需要观察的block,第二个参数是被通知block提交执行的queue,第三参数是当需要被通知执行的block,函数的原型:
void dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue,
dispatch_block_t notification_block);
具体的使用方法如下:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t previousBlock = dispatch_block_create(0, ^{
NSLog(@"previousBlock begin");
[NSThread sleepForTimeInterval:1];
NSLog(@"previousBlock done");
});
dispatch_async(queue, previousBlock);
dispatch_block_t notifyBlock = dispatch_block_create(0, ^{
NSLog(@"notifyBlock");
});
//当previousBlock执行完毕后,提交notifyBlock到global queue中执行
dispatch_block_notify(previousBlock, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), notifyBlock);
运行结果如下:
GCDTests[17129:895673] previousBlock begin
GCDTests[17129:895673] previousBlock done
GCDTests[17129:895673] notifyBlock
dispatch_block_cancel
之前在介绍nsopreration的时候提到它的一个优点是可以取消某个operation,现在在iOS8之后,提交到gcd队列中的dispatch block也可取消了,只需要简单的调用dispatch_block_cancel传入想要取消的block即可:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block1 = dispatch_block_create(0, ^{
NSLog(@"block1 begin");
[NSThread sleepForTimeInterval:1];
NSLog(@"block1 done");
});
dispatch_block_t block2 = dispatch_block_create(0, ^{
NSLog(@"block2 ");
});
dispatch_block_cancel(block2);
dispatch_async(queue, block1);
dispatch_async(queue, block2);
运行结果如下,block2不会再执行了,** (注意顺序一定要放对,将cancel放在async之前)**
2016-12-19 15:48:54.169 09-全局并发队列[9043:566646] block1 begin
2016-12-19 15:48:54.721 09-全局并发队列[9043:566646] block1 done
Dispatch Group
当我们想在gcd queue中所有的任务执行完毕之后做些特定事情的时候,也就是队列的同步问题,如果队列是串行的话,那将该操作最后添加到队列中即可,但如果队列是并行队列的话,这时候就可以利用dispatch_group来实现了,dispatch_group能很方便的解决同步的问题。dispatch_group_create可以创建一个group对象,然后可以添加block到该组里面,下面看下它的一些用法:
dispatch_group_wait
dispatch_group_wait会同步地等待group中所有的block执行完毕后才继续执行,类似于dispatch barrier
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
//将任务异步地添加到group中去执行
dispatch_group_async(group,queue,^{ NSLog(@"block1"); });
dispatch_group_async(group,queue,^{ NSLog(@"block2"); });
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
NSLog(@"go on");
执行结果如下,只有block1跟block2执行完毕后才会执行dispatch_group_wait后面的内容
GCDTests[954:41031] block2
GCDTests[954:41032] block1
GCDTests[954:40847] go on
dispatch_group_notify
功能与dispatch_group_wait类似,不过该过程是异步的,不会阻塞该线程,dispatch_group_notify有三个参数
void dispatch_group_notify(dispatch_group_t group, //要观察的group
dispatch_queue_t queue, //block执行的队列
dispatch_block_t block); //当group中所有任务执行完毕之后要执行的block
简单使用如下:
- (void)BlockDemo{
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,queue,^{ NSLog(@"block1,%@",[NSThread currentThread]); });
dispatch_group_async(group,queue,^{ NSLog(@"block2,%@",[NSThread currentThread]); });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done,%@",[NSThread currentThread]);
});
NSLog(@"go on");
}
打印结果如下:
2016-12-19 16:02:35.321 09-全局并发队列[9111:575287] go on
2016-12-19 16:02:35.321 09-全局并发队列[9111:575419] block1,<NSThread: 0x7ffbe2c08c10>{number = 2, name = (null)}
2016-12-19 16:02:35.321 09-全局并发队列[9111:575430] block2,<NSThread: 0x7ffbe2e01a00>{number = 3, name = (null)}
2016-12-19 16:02:35.322 09-全局并发队列[9111:575287] done,<NSThread: 0x7ffbe2d07360>{number = 1, name = main}
三:关于死锁的相关介绍
GCD避免死锁的三要素
平时总在用GCD,但你知不知道,GCD一不小心就会出现死锁,如果死锁在主线程上,整个程序就完了,所以避免死锁是我们则无旁贷的责任。
那我们先看看造成GCD死锁的三要素(以下内容是我个人总结,并写了一些测试用例,如有不对的地方,请大家指正 PS:我把GCD死锁分成 “普通死锁” ,“高级死锁”,"混合死锁")
普通死锁 必要条件
1.队列 串行队列:1 并行队列:0
2.调度方法 同步调用(dispatch_sync):1 异步调用(dispatch_async):0
3.同一个Q 当前所分发到的Q 和 "外部Q" 是否是同一个Q 同一个:1 不同:0
PS:"外部Q"是指
1.当前嵌套在外部的Q 如以下形式,Q1就是外部Q
dispatch_sync(Q1, ^{
NSLog(@"b");
dispatch_sync(Q2, ^{
NSLog(@"c");
});
});)
2.外部的方法执行时 所在的Q 如以下形式 dead1第二次运行时所在的Q是Q1 它就是外部Q
- (void)dead1{
NSLog(@"不死1");
__weak typeof (self) weakSelf = self;
dispatch_sync(Q1, ^{
NSLog(@"第二次运行死");
[weakSelf dead1];
});}
结论: 如果三个条件同时满足 则死锁,如果其中一个不满足 不会死锁
高级死锁 必要条件
1.调度方法 同步阻塞调用(dispatch_barrier_sync):1 异步阻塞调用(dispatch_barrier_async):0
2.同一个Q 当前所分发到的Q 和 "外部Q" 是否是同一个Q 同一个:1 不同:0
结论: 如果两个条件同时满足 则死锁,如果其中一个不满足 不会死锁 (高级死锁主要针对dispatch_barrier而言)
实例如下:
- (void)dispatchDemo3{
// 这里和使用什么queue没有关系
dispatch_queue_t q = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q, ^{
// 危险,可能导致线程爆炸以及死锁
for (int i = 0; i < 3; i++){
dispatch_async(q, ^{
NSLog(@"当前线程为%@,结果为%d",[NSThread currentThread],i);
});
}
// 关键是这里要是用dispatch_barrier_sync就肯定会发生死锁
dispatch_barrier_async(q, ^{
NSLog(@"么有死锁发生");
});
});
}
打印结果如下:
2016-12-19 16:32:59.255 09-全局并发队列[9234:589490] 当前线程为<NSThread: 0x7fe509c04fb0>{number = 2, name = (null)},结果为0
2016-12-19 16:32:59.255 09-全局并发队列[9234:589483] 当前线程为<NSThread: 0x7fe509d24e50>{number = 3, name = (null)},结果为1
2016-12-19 16:32:59.255 09-全局并发队列[9234:589494] 当前线程为<NSThread: 0x7fe509d0fd20>{number = 4, name = (null)},结果为2
2016-12-19 16:32:59.257 09-全局并发队列[9234:589494] 么有死锁发生
混合死锁 必要条件
1.调度方法 同步阻塞调用(dispatch_barrier_sync)或同步调用(dispatch_sync):1 异步阻塞调用(dispatch_barrier_async)或异步调用(dispatch_async):0
2.同一个Q 当前所分发到的Q 和 "外部Q" 是否是同一个Q 同一个:1 不同:0
结论: 如果两个条件同时满足 则死锁,如果其中一个不满足 不会死锁 (混合死锁主要针对dispatch_barrier 和 dispatch_async,dispatch_sync嵌套调用而言)
以下是死锁测试用例如下:
//混合模式,内部使用dispatch_sync 又是同一Q _concurrentQueue 死锁
- (void)dispatchDemo3{
// 这里和使用什么queue没有关系
dispatch_queue_t q = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
//混合模式,内部使用dispatch_sync 又是同一Q _concurrentQueue 死锁
NSLog(@"不死1");
dispatch_barrier_async(q, ^{
NSLog(@"不死2");
dispatch_sync(q, ^{
NSLog(@"死在子线程上");
});
});
}
打印结果如下:
2016-12-19 16:44:43.074 09-全局并发队列[9274:594740] 不死1
2016-12-19 16:44:43.075 09-全局并发队列[9274:594781] 不死2