有时候我们会有这样的需求:分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。
使用场景: 同时上传多个图片,所有图片上传完成之后去请求服务器上传图片地址。
dispatch_group_notify
原理:使用函数dispatch_group_create创建dispatch group,然后使用函数dispatch_group_async来将要执行的block任务提交到一个dispatch queue。同时将他们添加到一个组,等要执行的block任务全部执行完成之后,使用dispatch_group_notify函数接收完成时的消息。
使用示例:
/*** 队列组 dispatch_group_notify*/- (void)groupNotify {NSLog(@\"currentThread---%@\",[NSThread currentThread]); // 打印当前线程NSLog(@\"group---begin\");dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 追加任务1for (int i = 0; i < 2; ++i) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@\"1---%@\",[NSThread currentThread]); // 打印当前线程}});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 追加任务2for (int i = 0; i < 2; ++i) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@\"2---%@\",[NSThread currentThread]); // 打印当前线程}});dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务for (int i = 0; i < 2; ++i) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@\"3---%@\",[NSThread currentThread]); // 打印当前线程}NSLog(@\"group---end3\");});NSLog(@\"group---end\");}//输出结果2020-03-24 16:21:04.325304+0800 MyDemo[29802:1189401] currentThread---<NSThread: 0x280c33080>{number = 1, name = main}2020-03-24 16:21:04.325376+0800 MyDemo[29802:1189401] group---begin2020-03-24 16:21:04.325564+0800 MyDemo[29802:1189401] group---end2020-03-24 16:21:06.328662+0800 MyDemo[29802:1189417] 2---<NSThread: 0x280cbc340>{number = 8, name = (null)}2020-03-24 16:21:06.328754+0800 MyDemo[29802:1189421] 1---<NSThread: 0x280c6cd00>{number = 4, name = (null)}2020-03-24 16:21:08.329835+0800 MyDemo[29802:1189417] 2---<NSThread: 0x280cbc340>{number = 8, name = (null)}2020-03-24 16:21:08.334357+0800 MyDemo[29802:1189421] 1---<NSThread: 0x280c6cd00>{number = 4, name = (null)}2020-03-24 16:21:10.336270+0800 MyDemo[29802:1189401] 3---<NSThread: 0x280c33080>{number = 1, name = main}2020-03-24 16:21:12.338098+0800 MyDemo[29802:1189401] 3---<NSThread: 0x280c33080>{number = 1, name = main}2020-03-24 16:21:12.338583+0800 MyDemo[29802:1189401] group---end3
从dispatch_group_notify相关代码运行输出结果可以看出: 当所有任务都执行完成之后,才执行dispatch_group_notify block 中的任务。
dispatch_group_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
/*** 队列组 dispatch_group_wait*/- (void)groupWait {NSLog(@\"currentThread---%@\",[NSThread currentThread]); // 打印当前线程NSLog(@\"group---begin\");dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 追加任务1for (int i = 0; i < 2; ++i) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@\"1---%@\",[NSThread currentThread]); // 打印当前线程}});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 追加任务2for (int i = 0; i < 2; ++i) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@\"2---%@\",[NSThread currentThread]); // 打印当前线程}});// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(@\"group---end\");}//输出2020-03-24 16:17:53.231300+0800 MyDemo[29786:1188368] currentThread---<NSThread: 0x281297040>{number = 1, name = main}2020-03-24 16:17:53.231374+0800 MyDemo[29786:1188368] group---begin2020-03-24 16:17:55.232734+0800 MyDemo[29786:1188395] 1---<NSThread: 0x2812c4680>{number = 6, name = (null)}2020-03-24 16:17:55.236845+0800 MyDemo[29786:1188394] 2---<NSThread: 0x2812c8300>{number = 3, name = (null)}2020-03-24 16:17:57.238739+0800 MyDemo[29786:1188394] 2---<NSThread: 0x2812c8300>{number = 3, name = (null)}2020-03-24 16:17:57.238897+0800 MyDemo[29786:1188395] 1---<NSThread: 0x2812c4680>{number = 6, name = (null)}2020-03-24 16:17:57.239299+0800 MyDemo[29786:1188368] group---end
GCD中一些系统提供的常用dispatch方法
- dispatch_after
延时添加到队列
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);dispatch_after(delayTime, dispatch_get_main_queue(), ^{NSLog(@\"3秒之后执行\");});
- dispatch_apply
在给定的队列上多次执行某一任务,在主线程直接调用会阻塞主线程去执行block中的任务。dispatch_apply不会立刻返回,在执行完block中的任务后才会返回,是同步执行的函数。
注:嵌套使用dispatch_apply会导致死锁。
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);NSLog(@\"current task\");dispatch_async(globalQueue, ^{dispatch_queue_t applyQueue = dispatch_get_global_queue(0, 0);//第一个参数,3--block执行的次数//第二个参数,applyQueue--block任务提交到的队列//第三个参数,block--需要重复执行的任务dispatch_apply(3, applyQueue, ^(size_t index) {NSLog(@\"current index %@\",@(index));sleep(1);});NSLog(@\"dispatch_apply 执行完成\");dispatch_queue_t mainQueue = dispatch_get_main_queue();dispatch_async(mainQueue, ^{NSLog(@\"回到主线程更新UI\");});});NSLog(@\"next task\");//输出2020-03-24 16:36:05.564333+0800 MyDemo[29884:1193749] current task2020-03-24 16:36:05.564423+0800 MyDemo[29884:1193749] next task2020-03-24 16:36:05.564578+0800 MyDemo[29884:1193768] current index 02020-03-24 16:36:05.564750+0800 MyDemo[29884:1193767] current index 12020-03-24 16:36:06.565088+0800 MyDemo[29884:1193768] current index 22020-03-24 16:36:07.570245+0800 MyDemo[29884:1193768] dispatch_apply 执行完成2020-03-24 16:36:07.570582+0800 MyDemo[29884:1193749] 回到主线程更新UI
- dispatch_once
保证在app运行期间,block中的代码只执行一次(经典使用场景–单例)
static ShareManager *sharedManager = nil;//GCD实现单例功能+ (ShareManager *)shareManager{static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{sharedManager = [[self alloc] init];});return sharedManager;}
- dispatch_barrier_async 栅栏的作用
在并行队列中,等待在dispatch_barrier_async之前加入的队列全部执行完成之后(这些任务是并发执行的)再执行dispatch_barrier_async中的任务,dispatch_barrier_async中的任务执行完成之后,再去执行在dispatch_barrier_async之后加入到队列中的任务(这些任务是并发执行的)。
dispatch_queue_t conCurrentQueue = dispatch_queue_create(\"com.dullgrass.conCurrentQueue\", DISPATCH_QUEUE_CONCURRENT);dispatch_async(conCurrentQueue, ^{sleep(2);NSLog(@\"dispatch 1\");});dispatch_async(conCurrentQueue, ^{NSLog(@\"dispatch 2\");});dispatch_barrier_async(conCurrentQueue, ^{NSLog(@\"dispatch barrier\");});dispatch_async(conCurrentQueue, ^{NSLog(@\"dispatch 3\");});dispatch_async(conCurrentQueue, ^{NSLog(@\"dispatch 4\");});//输出2020-03-24 16:56:18.164157+0800 MyDemo[29966:1197675] dispatch 22020-03-24 16:56:20.169215+0800 MyDemo[29966:1197677] dispatch 12020-03-24 16:56:20.169871+0800 MyDemo[29966:1197677] dispatch barrier2020-03-24 16:56:20.170382+0800 MyDemo[29966:1197677] dispatch 32020-03-24 16:56:20.170429+0800 MyDemo[29966:1197675] dispatch 4
- dispatch_semaphore 信号量
Dispatch Semaphore 在实际开发中主要用于:
- 保持线程同步,将异步执行任务转换为同步执行任务
- 保证线程安全,为线程加锁
- dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
- dispatch_semaphore_signal:发送一个信号,让信号总量加1
- dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
/*** 线程安全:使用 semaphore 加锁* 初始化火车票数量、卖票窗口(线程安全)、并开始卖票*/- (void)initTicketStatusSave {NSLog(@\"currentThread---%@\",[NSThread currentThread]); // 打印当前线程NSLog(@\"semaphore---begin\");semaphoreLock = dispatch_semaphore_create(1);self.ticketSurplusCount = 50;// queue1 代表北京火车票售卖窗口dispatch_queue_t queue1 = dispatch_queue_create(\"net.bujige.testQueue1\", DISPATCH_QUEUE_SERIAL);// queue2 代表上海火车票售卖窗口dispatch_queue_t queue2 = dispatch_queue_create(\"net.bujige.testQueue2\", DISPATCH_QUEUE_SERIAL);__weak typeof(self) weakSelf = self;dispatch_async(queue1, ^{[weakSelf saleTicketSafe];});dispatch_async(queue2, ^{[weakSelf saleTicketSafe];});}/*** 售卖火车票(线程安全)*/- (void)saleTicketSafe {while (1) {// 相当于加锁dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);if (self.ticketSurplusCount > 0) { //如果还有票,继续售卖self.ticketSurplusCount--;NSLog(@\"%@\", [NSString stringWithFormat:@\"剩余票数:%d 窗口:%@\", self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else { //如果已卖完,关闭售票窗口NSLog(@\"所有火车票均已售完\");// 相当于解锁dispatch_semaphore_signal(semaphoreLock);break;}// 相当于解锁dispatch_semaphore_signal(semaphoreLock);}}输出结果为:2018-02-23 22:32:19.814232+0800 YSC-GCD-demo[20862:5290531] currentThread---<NSThread: 0x6000000783c0>{number = 1, name = main}2018-02-23 22:32:19.814412+0800 YSC-GCD-demo[20862:5290531] semaphore---begin2018-02-23 22:32:19.814837+0800 YSC-GCD-demo[20862:5290687] 剩余票数:49 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}2018-02-23 22:32:20.017745+0800 YSC-GCD-demo[20862:5290689] 剩余票数:48 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}2018-02-23 22:32:20.222039+0800 YSC-GCD-demo[20862:5290687] 剩余票数:47 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}......2018-02-23 22:32:29.024817+0800 YSC-GCD-demo[20862:5290689] 剩余票数:4 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}2018-02-23 22:32:29.230110+0800 YSC-GCD-demo[20862:5290687] 剩余票数:3 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}2018-02-23 22:32:29.433615+0800 YSC-GCD-demo[20862:5290689] 剩余票数:2 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}2018-02-23 22:32:29.637572+0800 YSC-GCD-demo[20862:5290687] 剩余票数:1 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}2018-02-23 22:32:29.840234+0800 YSC-GCD-demo[20862:5290689] 剩余票数:0 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}2018-02-23 22:32:30.044960+0800 YSC-GCD-demo[20862:5290687] 所有火车票均已售完2018-02-23 22:32:30.045260+0800 YSC-GCD-demo[20862:5290689] 所有火车票均已售完
声明:本文非原创,仅仅整理一些开发技能知识文章,以作存档学习用
参考:
- iOS中GCD的使用小结
- iOS GCD (二 ) dispatch_group 队列组
- iOS GCD (四) dispatch_semaphore 信号量
- 点赞1
- 收藏
- 分享
- 文章举报
春风酿酒发布了12 篇原创文章 · 获赞 12 · 访问量 208私信关注