前言:
在日常的开发中,肯定会经常遇到这样的场景,一个页面中需要同时去调用好几个接口,等这几个接口调用完成之后,再次执行一些事情(比如说刷新页面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
不会影响阻塞主线程。
今天就到这里了,欢迎交流分享~~~