GCD调度组(dispatch_group)的使用
GCD是Grand Central Dispatch的缩写,它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。日常开发中,GCD可以说是无处不在,我们无时无刻都在用到它。由于之前的工作内容原因,对dispatch_group一直停留在理论阶段,没有对其进行认真的实践,如今换了新的公司,投入到新项目中的开发时,才发现自己对于dispatch_group的认识是如此的浅薄,借此空档时间,重新认识调度组并以此文章仅作为笔记,也算是对于近期使用调度组的总结。
调度组中常用的函数
- dispatch_group_create(void);
初始化一个调度组,创建成功返回dispatch_group调度组,失败返回NULL - void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
提交一个任务(block)到指定的queue中,并关联到指定的group调度组- group 指定的调度组,block的关联调度组
- queue block指定的队列
- block 提交到指定队列的block 通过typedef void (^dispatch_block_t)(void);该函数无法给block传递参数
- void dispatch_group_async_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
提交一个函数指针(dispatch_function_t)到queue中,并关联到指定的group调度组- group 指定的调度组,block的关联调度组
- queue block指定的队列
- context 传递到函数中的的参数
- work 在指定的queue中的指定函数。
- void dispatch_group_enter(dispatch_group_t group);
- void dispatch_group_leave(dispatch_group_t group);
将一个block提交到指定的queue上并关联到group调度组.两个函数必须成对出现,如果leave比enter多,则将造成内存泄露 - dispatch_group_wait(<#dispatch_group_t_Nonnullgroup#>,<#dispatch_time_t timeout#>)
执行等待,等待所有关联到group调度组的block执行完成,或者等待timeout发生超时,当在超时时间timeout内执行完了所有的block函数,则返回0,否则返回非0值,只有执行完了此函数,此函数后面的方法才会执行- group 给定调度组
- timeout 如果group调度组里边的block执行时间非常长,函数的等待时间.
- void
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
该函数指定了一个block,当group调度组里边的所有block都执行完成时,将通知block关联到group中,并加入到给定的queue队列里,当group调度组当前没有任何block关联的时候将立即将block提交到queue队列,并与group调度组关联,该函数返回void.- group 给定的调度组
- queue 给定的队列.
- block 给定的闭包函数.
- void dispatch_group_notify_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
与disptch_group_notify类似,提交的一个函数work作为执行体,context是执行时传递的参数,该函数返回void.
理论需要实践
- 通过dispatch_group_asyn的方法将任务添加到队列中并将任务关联到调度组中
//通过dispatch_group_asyn的方法将任务添加到队列中
- (void)asyncAddTaskToQueueAndGroupByDispatch_group {
//创建调度组
dispatch_group_t group = dispatch_group_create();
//创建指定的队列
//串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("net.jihuigou.serialQueue", DISPATCH_QUEUE_SERIAL);
//串行队列之主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("net.jihuigou.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = concurrentQueue;
//将任务添加到队列中并关联调度组
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"任务1---------被执行-%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2--------被执行-%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"任务3-------被执行-%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"任务4-------被执行-%@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"当前任务均处理完成---%@",[NSThread currentThread]);
});
NSLog(@"代码执行到这了");
}
输出结果:
2019-10-29 11:43:08.619037+0800 RACDemo[31421:474129] 代码执行到这了
2019-10-29 11:43:08.619120+0800 RACDemo[31421:474181] 任务4-------被执行-<NSThread: 0x600003fc5c80>{number = 3, name = (null)}
2019-10-29 11:43:10.622480+0800 RACDemo[31421:474182] 任务1---------被执行-<NSThread: 0x600003fc85c0>{number = 4, name = (null)}
2019-10-29 11:43:10.622480+0800 RACDemo[31421:474188] 任务2--------被执行-<NSThread: 0x600003fccd80>{number = 5, name = (null)}
2019-10-29 11:43:10.622503+0800 RACDemo[31421:474187] 任务3-------被执行-<NSThread: 0x600003ff4540>{number = 6, name = (null)}
2019-10-29 11:43:10.622726+0800 RACDemo[31421:474129] 当前任务均处理完成---<NSThread: 0x600003fa2480>{number = 1, name = main}
此处使用并发队列作为任务添加的指定队列,从输出结果中可以看出,任务执行顺序不定,并且开启了多条线程,并且首先先打印了dispatch_group_notify后面的结果。只有调度组中关联的所有任务都完成,才会执行dispatch_group_notify方法中的block,
- dispatch_group_enter&dispatch_group_leave
- (void)dispatchGroupEnterAndLeaveDemo {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t serialQueue = dispatch_queue_create("net.jihuigou.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t concurrentQueue = dispatch_queue_create("net.jihuigou.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = concurrentQueue;
dispatch_group_enter(group);
//将任务添加到队列中并关联调度组
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"任务1---------被执行-%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2--------被执行-%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"任务3-------被执行-%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"任务4-------被执行-%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"当前任务均处理完成---%@",[NSThread currentThread]);
});
NSLog(@"代码执行到这了");
}
输出结果:
2019-10-29 13:45:47.183447+0800 RACDemo[32346:503834] 代码执行到这了
2019-10-29 13:45:47.183523+0800 RACDemo[32346:503918] 任务4-------被执行-<NSThread: 0x6000037b3100>{number = 3, name = (null)}
2019-10-29 13:45:49.186933+0800 RACDemo[32346:503913] 任务3-------被执行-<NSThread: 0x6000037a9ec0>{number = 4, name = (null)}
2019-10-29 13:45:49.186999+0800 RACDemo[32346:503914] 任务1---------被执行-<NSThread: 0x6000037a9ac0>{number = 5, name = (null)}
2019-10-29 13:45:49.187004+0800 RACDemo[32346:503912] 任务2--------被执行-<NSThread: 0x600003783600>{number = 6, name = (null)}
2019-10-29 13:45:49.187255+0800 RACDemo[32346:503834] 当前任务均处理完成---<NSThread: 0x6000037e28c0>{number = 1, name = main}
这里的输出结果与上一个结果类似,dispatch_group_notify中block的内容等调度组中所有的任务都结束才执行,与dispatch_group_async不同的是,这里dispatch_group_enter是进组,相当于给调度组添加任务,调度组内的任务数+1,dispatch_group_leave是出组,调度组内的任务数-1,相当于该任务已经完成,只有enter和leave此处相同,即调度组内的任务数为0,才会收到调度组任务完成的通知,执行dispatch_group_notify
注意的点
- 如果使用dispatch_group_async的方式将任务添加到队列并关联到调度组中,添加的任务里应又存在异步的操作,这样打印结果是否和上面的一样呢?
- (void)dispatchGroupAsyncDemo {
//创建调度组
dispatch_group_t group = dispatch_group_create();
//创建指定的队列
//串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("net.jihuigou.serialQueue", DISPATCH_QUEUE_SERIAL);
//串行队列之主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("net.jihuigou.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = concurrentQueue;
//将任务添加到队列中并关联调度组
dispatch_group_async(group, queue, ^{
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];//模拟异步操作
NSLog(@"任务1---------被执行-%@",[NSThread currentThread]);
});
});
dispatch_group_async(group, queue, ^{
NSLog(@"任务2--------被执行-%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"任务3-------被执行-%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"任务4-------被执行-%@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"当前任务均处理完成---%@",[NSThread currentThread]);
});
NSLog(@"代码执行到这了");
}
输出结果
2019-10-29 14:25:21.025384+0800 RACDemo[33234:521186] 任务2--------被执行-<NSThread: 0x6000031d0dc0>{number = 3, name = (null)}
2019-10-29 14:25:21.025531+0800 RACDemo[33234:521187] 任务3-------被执行-<NSThread: 0x6000031d0f80>{number = 4, name = (null)}
2019-10-29 14:25:21.025699+0800 RACDemo[33234:521187] 任务4-------被执行-<NSThread: 0x6000031d0f80>{number = 4, name = (null)}
2019-10-29 14:25:21.025676+0800 RACDemo[33234:521134] 代码执行到这了
2019-10-29 14:25:21.058502+0800 RACDemo[33234:521134] 当前任务均处理完成---<NSThread: 0x6000031be5c0>{number = 1, name = main}
2019-10-29 14:25:23.029060+0800 RACDemo[33234:521190] 任务1---------被执行-<NSThread: 0x6000031d9300>{number = 5, name = (null)}
从输出结果,我们可以看出,任务1的打印在dispatch_group_notify的打印之后,这样的结果明显不是我们使用调度组所希望的,所以,在使用dispatch_group_async添加任务时,被添加的任务不应该存在异步的操作,对于任务中存在异步的操作,解决方案是使用手动的进组出组方式,即dispatch_group_enter() dispatch_group_leave(),这样就达到我们所想的效果了
这里是修改后的任务1
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];//模拟异步操作
NSLog(@"任务1---------被执行-%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
});
输出结果:
2019-10-29 14:30:35.063698+0800 RACDemo[33393:523444] 代码执行到这了
2019-10-29 14:30:35.063768+0800 RACDemo[33393:523505] 任务2--------被执行-<NSThread: 0x600000111c80>{number = 3, name = (null)}
2019-10-29 14:30:35.063770+0800 RACDemo[33393:523503] 任务3-------被执行-<NSThread: 0x600000125e00>{number = 4, name = (null)}
2019-10-29 14:30:35.063770+0800 RACDemo[33393:523506] 任务4-------被执行-<NSThread: 0x600000117c40>{number = 5, name = (null)}
2019-10-29 14:30:37.068327+0800 RACDemo[33393:523507] 任务1---------被执行-<NSThread: 0x60000012b680>{number = 6, name = (null)}
2019-10-29 14:30:37.068589+0800 RACDemo[33393:523444] 当前任务均处理完成---<NSThread: 0x6000001610c0>{number = 1, name = main}
- dispatch_group_notify和dispatch_group_wait的区别
dispatch_group_notify和dispatch_group_wait都是等待调度组中所有的任务都完成之后才执行各自block中的内容,不同的是,dispatch_group_notify是通过通知来实现的,不会阻塞主线程,而dispatch_group_wait是同步的,只有等到调度组中所有的任务都完成了,并且dispatch_group_wiat也执行了,才会接着执行dispatch_group_wait之后的代码
以上即我近期使用dispatch_group的一些经验和心得,学生资历浅薄,若有说的不对的地方还望各位大佬指出,学生定及时改正。