前言:
在日常的开发中,肯定会经常遇到这样的场景,一个页面中需要同时去调用好几个接口,等这几个接口调用完成之后,再次执行一些事情(比如说刷新页面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(@"全部都执行完了");
});
控制台输出结果

解读:
代码执行打印开始了之后进入第一个enter,group的引用计数+1然后,在第一个子线程dispatch_async中执行sleep(3); ZDLog(@"第一个任务开始了");,主线程执行wait,这时页面会卡住,但是在3s时会打印第一个dispatch_async中的结果,5s后,打印ZDLog(@"等待");执行leave,group的引用计数-1,之后进入第二个enter,group的引用计数又+1,进入第二个子线程dispatch_async中执行sleep(10); ZDLog(@"第二个任务开始了");,执行leave,group的引用计数-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(@"全部都执行完了");
});
控制台输出结果

解读:
代码执行打印开始了之后进入第一个enter,group的引用计数+1然后,在第一个子线程dispatch_async中执行sleep(3); ZDLog(@"第一个任务开始了");,主线程执行wait,这时页面会卡住,但是在3s时会打印第一个dispatch_async中的结果,同时执行leave,group的引用计数-1,wait解除,进入第二个enter,group的引用计数又+1,进入第二个子线程dispatch_async中执行sleep(10); ZDLog(@"第二个任务开始了");,执行leave,group的引用计数-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(@"等待");
});
控制台输出结果

解读:
因为没有enter和leave的存在,所以代码会依次进行,结果就是第二个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(@"等待");
});
控制台输出结果

解读:
因为没有
enter和leave的存在,所以dispatch_group_notify会在引用计数为0之后才会执行,结果就是第二个dispatch_async执行完后才会执行dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{ ZDLog(@"全部都执行完了"); });,而最后一个子线程dispatch_async中的wait会在5 *NSEC_PER_SEC会执行ZDLog(@"等待");要点:在子线程中执行
wait不会影响阻塞主线程。
今天就到这里了,欢迎交流分享~~~