GCD调度组的使用

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调度组
    1. group 指定的调度组,block的关联调度组
    2. queue block指定的队列
    3. 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调度组
    1. group 指定的调度组,block的关联调度组
    2. queue block指定的队列
    3. context 传递到函数中的的参数
    4. 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值,只有执行完了此函数,此函数后面的方法才会执行
    1. group 给定调度组
    2. 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.
    1. group 给定的调度组
    2. queue 给定的队列.
    3. 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的一些经验和心得,学生资历浅薄,若有说的不对的地方还望各位大佬指出,学生定及时改正。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容