最近在使用dispatch_barrier_async时踩到了坑,随即仔细了解了一下dispatch_barrier_async,发现了不少有意思的事情,下面且听我一一道来。
平常的开发过程中,应该有不少童鞋遇到过这样的需求:分别有三个异步任务A、B、C,要求任务A、B全部执行完毕之后才执行C;面对这个需求,Dispatch Group是很多人的选择,而为了追求更简便的操作,dispatch_barrier_async应该更受青睐。
好了,既然决定使用dispatch_barrier_async,那么来吧,说干就干,很简单的几行代码,马上写出来了:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"任务A");
});
dispatch_async(queue, ^{
NSLog(@"任务B");
});
dispatch_barrier_async(queue, ^{
NSLog(@"任务C");
});
好了,美滋滋的喝口饮料,等待程序执行;这么简单的东西,还能出错?搞笑呢是吧!哼哼,等着看吧,肯定是打印A、B后,最后打印出执行C。刚喝完一口饮料,看到打印结果,顿时喷了出来......
2018-03-17 15:19:16.231556+0800 dispatch_barriar_test[4274:331672] 任务C
2018-03-17 15:19:16.231569+0800 dispatch_barriar_test[4274:331836] 任务A
2018-03-17 15:19:16.231569+0800 dispatch_barriar_test[4274:331833] 任务B
啊?眼花了?编译器疯了?还是...我疯了?
看了下代码,创建并发队列,然后添加任务A、B,最后一个dispatch_barrier_async进行同步节点执行任务C,没问题啊,这怎么回事儿?跟想象的不一样,赶紧查了下该方法的官方注释,发现了一个“坑爹”的解释:
翻译过来的意思就是:
如果提交的是全局并发队列或未使用DISPATCH_QUEUE_CONCURRENT属性创建的队列(意即创建了一个串行队列)时,dispatch_barrier 与dispatch_async()/ dispatch_sync()API提交的block的行为相同。
也就是使用dispatch_barrier时,是不能使用dispatch_get_global_queue的,赶紧换成dispatch_queue_create创建一个并发队列,如下:
dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务A");
});
dispatch_async(queue, ^{
NSLog(@"任务B");
});
dispatch_barrier_async(queue, ^{
NSLog(@"任务C");
});
这下正常了,不经意的一个小问题,导致大问题的出现,谨记谨记。
查看官方文档时候,发现了dispatch_barrier_async和dispatch_barrier_sync两个方法,以前只是使用过dispatch_barrier_async方法,而dispatch_barrier_sync却比较陌生,于是比较了两个方法的功能,又有了新的发现。
首先使用dispatch_barrier_async进行测试
dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务1");
});
dispatch_async(queue, ^{
NSLog(@"任务2");
});
dispatch_barrier_async(queue, ^{
sleep(3);
NSLog(@"barrier");
});
dispatch_async(queue, ^{
NSLog(@"任务4");
});
打印结果如下:
2018-03-17 15:40:18.736130+0800 dispatch_barriar_test[4542:365066] 任务2
2018-03-17 15:40:18.736130+0800 dispatch_barriar_test[4542:365062] 任务1
2018-03-17 15:40:21.737863+0800 dispatch_barriar_test[4542:365062] barrier
2018-03-17 15:40:21.738172+0800 dispatch_barriar_test[4542:365062] 任务4
然后使用dispatch_barrier_sync测试
dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务1");
});
dispatch_async(queue, ^{
NSLog(@"任务2");
});
dispatch_barrier_sync(queue, ^{
sleep(3);
NSLog(@"barrier");
});
dispatch_async(queue, ^{
NSLog(@"任务4");
});
咦?怎么一样?难道两个没有区别?不会的,苹果怎么可能给同一个API两个不同的名称?既然不同肯定有区别。接着实验,还是先实验dispatch_barrier_async:
dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务1");
});
dispatch_async(queue, ^{
NSLog(@"任务2");
});
dispatch_barrier_async(queue, ^{
sleep(3);
NSLog(@"barrier");
});
NSLog(@"来到了这里");
dispatch_async(queue, ^{
NSLog(@"任务4");
});
打印结果(注意NSLog(@"来到了这里");
这行代码的执行):
2018-03-17 15:45:08.918462+0800 dispatch_barriar_test[4647:374019] 任务1
2018-03-17 15:45:08.918462+0800 dispatch_barriar_test[4647:373957] 来到了这里
2018-03-17 15:45:08.918462+0800 dispatch_barriar_test[4647:374023] 任务2
2018-03-17 15:45:11.919786+0800 dispatch_barriar_test[4647:374023] barrier
2018-03-17 15:45:11.920103+0800 dispatch_barriar_test[4647:374023] 任务4
接着实验dispatch_barrier_sync:
dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务1");
});
dispatch_async(queue, ^{
NSLog(@"任务2");
});
dispatch_barrier_sync(queue, ^{
sleep(3);
NSLog(@"barrier");
});
NSLog(@"来到了这里");
dispatch_async(queue, ^{
NSLog(@"任务4");
});
打印结果:
2018-03-17 15:48:13.325138+0800 dispatch_barriar_test[4714:380014] 任务1
2018-03-17 15:48:13.325141+0800 dispatch_barriar_test[4714:380011] 任务2
2018-03-17 15:48:16.325626+0800 dispatch_barriar_test[4714:379926] barrier
2018-03-17 15:48:16.325873+0800 dispatch_barriar_test[4714:379926] 来到了这里
2018-03-17 15:48:16.326083+0800 dispatch_barriar_test[4714:380011] 任务4
区别来了,"来到了这里"这句话执行时机不同,async不会阻塞主线程后面的代码执行,而sync则是会将本身block中的任务执行完毕后,程序才会接着执行sync之后的代码。总结如下:
dispatch_barrier_sync和dispatch_barrier_async的共同点:
1、都会等待在它前面插入队列的任务(A、B)先执行完
2、都会等待他们自己的任务(barrier)执行完再执行后面的任务(C)
dispatch_barrier_sync和dispatch_barrier_async的不共同点:
在将任务插入到queue的时候,dispatch_barrier_sync需要等待自己的任务(barrier)结束之后才会继续程序,然后插入被写在它后面的任务(C),然后执行任务(C)
而dispatch_barrier_async将自己的任务(barrier)插入到queue之后,不会等待自己的任务结束,程序继续执行,会继续把后面的任务(C)插入到queue。
所以,dispatch_barrier_async的不等待(异步)特性体现在将任务插入队列的过程,它的等待特性体现在任务真正执行的过程。
附录(也算是追加)
也许会对并行、串行、异步、同步有所理解不深。
并行:就是队列里面的任务(代码块,block)不是一个个执行,而是并发执行,也就是可以同时执行的意思
串行:队列里面的任务一个接着一个执行,要等前一个任务结束,下一个任务才可以执行
异步:具有新开线程的能力
同步:不具有新开线程的能力,只能在当前线程执行任务
那么,如果他们相互串起来,会怎么样呢?
并行+异步:就是真正的并发,新开有有多个线程处理任务,任务并发执行(不按顺序执行)
串行+异步:新开一个线程,任务一个接一个执行,上一个任务处理完毕,下一个任务才可以被执行
并行+同步:不新开线程,任务一个接一个执行
串行+同步:不新开线程,任务一个接一个执行
参考文献:https://www.cnblogs.com/ziyi--caolu/p/4900650.html
http://blog.csdn.net/u013046795/article/details/47057585