美文网首页selector
浅谈dispatch_barrier_(a)sync

浅谈dispatch_barrier_(a)sync

作者: 风尘子 | 来源:发表于2018-03-17 16:00 被阅读16次

    最近在使用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

    相关文章

      网友评论

        本文标题:浅谈dispatch_barrier_(a)sync

        本文链接:https://www.haomeiwen.com/subject/ptjjqftx.html