美文网首页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实现多线程的同步

    前言: 在日常的开发中,肯定会经常遇到这样的场景,一个页面中需要同时去调用好几个接口,等这几个接口调用完成之后,再...

  • iOS Condition

    condition实现多线程的同步用法:

  • OC语法_多线程

    1. 多线程实现原理; 2. 多线程实现的方案; 3. 线程同步技术; 1. 多线程实现原理; - 进程:...

  • iOS 多线程基础

    转自:iOS 多线程基础 - 简书 多线程同步和异步的区别?IOS中如何实现多线程的同步? 异步:可以同时执行多条...

  • iOS_2016最新版面试题(附答案)

    每天四道题,让精彩填满生活... 1、多线程同步和异步的区别。iOS如何实现多线程的同步? 答:同步就是指一个线程...

  • 蚂蚁Java互联网架构师第1期高端

    教程目录 ├─0001-多线程快速入门.zip ├─0002-多线程之间实现同步.zip ├─0003--多线程之...

  • 线程锁

    探讨iOS开发中各种锁使用NSCondition实现多线程同步 NSCondition是线程同步, 阻塞线程。 取...

  • 29_SDL 多线程与锁机制

    一、简介 为什么要用多线程?在音视频领域主要是实现音视频同步。实现了音视频同步,我们的播放器就基本上合格了。多线程...

  • 2020-07-02【多线程】

    进程 线程 多线程的实现方式1 设置/获取线程名称 线程调度 线程控制 线程生命周期 多线程实现方式2 练习 同步...

  • Python 3 多线程编程

    本文主要基于python 3.5实现多线程编程 1. 创建多线程 2. 多线程间的同步,lock机制 3. que...

网友评论

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

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