美文网首页iOS开发iOS Developer程序员
5.dispatch_group实现多线程的同步

5.dispatch_group实现多线程的同步

作者: Lee丶Way | 来源:发表于2017-03-09 20:16 被阅读141次
    前言:

    在日常的开发中,肯定会经常遇到这样的场景,一个页面中需要同时去调用好几个接口,等这几个接口调用完成之后,再次执行一些事情(比如说刷新页面reloadData,启用Button等等),像这种多线程的同步有很多种解决办法:例如写几个标识,每一个接口调用完就TRUE一个,当所有的都是TRUE之后,再去进行操作;用NSOperationQueue也可以。今天我这里说的是利用GCD中的dispatch_group调度组实现。如果不知道GCD是什么,那你就先去问问度娘。

    实现这个场景,同学你需要掌握以下几个技能

    1. dispatch_group_async
    dispatch_group_async(dispatch_group_t ,
                             dispatch_queue_t ,
                             ^(void)block)
    

    解读这个方法:将这个代码块block加到队列dispatch_queue_t中,并且与调用组dispatch_group_t关联。
    说白了就是在调用组dispatch_group_t和队列dispatch_queue_t中执行代码块block,当代码块block执行完毕后,会调用dispatch_group_notify方法,同时dispatch_group_wait会执行。

    2. dispatch_group_notify
    dispatch_group_notify(dispatch_group_t ,
                          dispatch_queue_t,
                          ^(void)block)
    

    解读这个方法:dispatch_queue_t中所有任务执行完毕后,会执行这个代码块block
    我们一般就是在这个方法中去做那件最后要做的事情。

    3. dispatch_group_enter
    dispatch_group_enter(dispatch_group_t  )
    

    解读这个方法:enter就是进入到某个调用组dispatch_group_t中,enter的出现就必须搭配leave
    我个人把他理解成MRC中的retain关键字,给group引用计数+1

    4. dispatch_group_leave
    dispatch_group_leave(dispatch_group_t  )
    

    解读这个方法:leave就是进入到某个调用组dispatch_group_t中,leave的出现就必须搭配enter
    我个人把他理解成MRC中的release关键字,给group引用计数-1,当引用计数为0了之后就会调用dispatch_group_notify

    5. dispatch_group_wait
    dispatch_group_wait(dispatch_group_t  , dispatch_time_t )
    

    解读这个方法:可以设置一个超时时间dispatch_time_t,意思就是wait会阻塞主线程,等待 dispatch_group中的任务执行,当执行完毕后,或者超过了dispatch_time_t设置的时间,就会结束这个方法,执行剩下的任务。

    场景

    我会用同一份代码,更改不同代码的位置来详细讲述上述的方法,请仔细区分
    1
        dispatch_group_t serviceGroup = dispatch_group_create();
        ZDLog(@"开始了");
        dispatch_group_enter(serviceGroup);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            ZDLog(@"第一个任务开始了");
        });
        dispatch_group_wait(serviceGroup, dispatch_time(DISPATCH_TIME_NOW, 5 *NSEC_PER_SEC));
        ZDLog(@"等待");
        dispatch_group_leave(serviceGroup);
        dispatch_group_enter(serviceGroup);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(10);
            ZDLog(@"第二个任务开始了");
            dispatch_group_leave(serviceGroup);
        });
        dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{
            ZDLog(@"全部都执行完了");
        });
    

    控制台输出结果

    测试输出结果的时间.png

    解读:
    代码执行打印开始了之后进入第一个entergroup的引用计数+1然后,在第一个子线程dispatch_async中执行sleep(3); ZDLog(@"第一个任务开始了");,主线程执行wait,这时页面会卡住,但是在3s时会打印第一个dispatch_async中的结果,5s后,打印ZDLog(@"等待");执行leavegroup的引用计数-1,之后进入第二个entergroup的引用计数又+1,进入第二个子线程dispatch_async中执行sleep(10); ZDLog(@"第二个任务开始了");,执行leavegroup的引用计数-1,引用计数为0,调用dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{ ZDLog(@"全部都执行完了"); });
    要点:
    执行dispatch_group_wait会阻塞主线程,影响wait的两个因素中,引用计数为0晚于dispatch_time_t,故wait依照dispatch_time_t执行。

    2
        dispatch_group_t serviceGroup = dispatch_group_create();
        ZDLog(@"开始了");
        dispatch_group_enter(serviceGroup);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            ZDLog(@"第一个任务开始了");
            dispatch_group_leave(serviceGroup);
        });
        dispatch_group_wait(serviceGroup, dispatch_time(DISPATCH_TIME_NOW, 5 *NSEC_PER_SEC));
        ZDLog(@"等待");
        dispatch_group_enter(serviceGroup);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(10);
            ZDLog(@"第二个任务开始了");
            dispatch_group_leave(serviceGroup);
        });
        dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{
            ZDLog(@"全部都执行完了");
        });
    

    控制台输出结果

    测试输出结果的时间.png

    解读:
    代码执行打印开始了之后进入第一个entergroup的引用计数+1然后,在第一个子线程dispatch_async中执行sleep(3); ZDLog(@"第一个任务开始了");,主线程执行wait,这时页面会卡住,但是在3s时会打印第一个dispatch_async中的结果,同时执行leavegroup的引用计数-1wait解除,进入第二个entergroup的引用计数又+1,进入第二个子线程dispatch_async中执行sleep(10); ZDLog(@"第二个任务开始了");,执行leavegroup的引用计数-1,引用计数为0,调用dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{ ZDLog(@"全部都执行完了"); });
    要点:
    执行dispatch_group_wait会阻塞主线程,影响wait的两个因素中,引用计数为0早于dispatch_time_t,故wait依照引用计数为0执行。

    3
        dispatch_group_t serviceGroup = dispatch_group_create();
        ZDLog(@"开始了");
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            ZDLog(@"第一个任务开始了");
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(10);
            ZDLog(@"第二个任务开始了");
        });
        dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{
            ZDLog(@"全部都执行完了");
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_group_wait(serviceGroup, dispatch_time(DISPATCH_TIME_NOW, 5 *NSEC_PER_SEC));
            ZDLog(@"等待");
        });
    

    控制台输出结果

    测试输出结果的时间.png

    解读:
    因为没有enterleave的存在,所以代码会依次进行,结果就是第二个dispatch_async会比第一个dispatch_async晚执行sleep(10)
    要点:在子线程中执行wait不会影响阻塞主线程。

    4
        dispatch_group_t serviceGroup = dispatch_group_create();
        ZDLog(@"开始了");
        dispatch_group_enter(serviceGroup);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            ZDLog(@"第一个任务开始了");
            dispatch_group_leave(serviceGroup);
        });
        dispatch_group_enter(serviceGroup);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(10);
            ZDLog(@"第二个任务开始了");
            dispatch_group_leave(serviceGroup);
        });
        dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{
            ZDLog(@"全部都执行完了");
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_group_wait(serviceGroup, dispatch_time(DISPATCH_TIME_NOW, 5 *NSEC_PER_SEC));
            ZDLog(@"等待");
        });
    

    控制台输出结果

    测试输出结果的时间.png
    解读:
    因为没有enterleave的存在,所以dispatch_group_notify会在引用计数为0之后才会执行,结果就是第二个dispatch_async执行完后才会执行dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{ ZDLog(@"全部都执行完了"); });,而最后一个子线程dispatch_async中的wait会在5 *NSEC_PER_SEC会执行ZDLog(@"等待");
    要点:在子线程中执行wait不会影响阻塞主线程。

    今天就到这里了,欢迎交流分享~~~

    相关文章

      网友评论

        本文标题:5.dispatch_group实现多线程的同步

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