Dispatch Group总结

这一篇其实在博客里已经发过了,这里凑个数再发一次。

前段时间看叶孤城开源的下厨房app,在网络块看到它使用了Dispatch Group,想起来之前虽然看过一点但是从没用过,忘得差不多了,正好乘此机会再了解一下。而我们公司的app,最近正好有个需求我也用到了这个,自己这里做个总结。

Dispatch Group介绍和使用方法

下面是我查到的关于Dispatch Group的一个解释和作用说明

Dispatch Group,译作“派发分组”或“调度租”,是GCD的一种特性,能够把任务分组。调用者可以等待这组任务执行完成,也可以提供调用函数之后继续往下执行,这组任务完成是,调用者会得到通知。

关键点在任务分组任务完成后得到通知,立马想到的一个需求场景是下载队列,将多个下载任务加到Dispatch Group中,全部下载完成后再执行相应的操作。下面看一下使用方法:

声明并创建一个Dispatch Group:
dispatch_group_t group = dispatch_group_create();
类似于创建派发队列,只是少了个标识符,然后将任务分组,这一步有两种方法

 void dispatch_group_async(
   dispatch_group_t group,
   dispatch_queue_t queue,
   dispatch_block_t block);

这种方法是dispatch_async函数的变体,多了一个参数,表示待执行块所属的的组,另一种分组的方法是一对函数:

void dispatch_group_enter(dispatch_group_t group);
void dispatch_group_leave(dispatch_group_t group);

enter可以是分组里正在执行的任务数加一,leave可以使之减一。由此易知这两个函数可以配对使用,类似内存管理的的retain和release。

下面这个函数则用于等待Dispatch Group任务执行完毕

long dispatch_group_wait(dispatch_group_t group,
                         dispatch_time_t timeout); 

这个函数接受两个参数,第一个表示等待的Group,第二个表示等待的时间,等待时会阻塞线程,此外也可以用DISPATCH_TIME_FORVER表示永久等待.

除了上面所列的那个函数等待Dispatch Group执行完毕之外,也可以换个办法,使用下列函数:

void dispatch_group_notify(dispatch_group_t group,
                           dispatch_queue_t queue,
                           dispatch_block_t block);

wait不同是可以可以传入queue和block,等Group上的执行完毕后在指定的queue中执行block,且这个不如阻塞线程,例如在其他线程处理数据,处理完成得到通知在主线程改变UI。

例如想在一个for循环中每次循环都执行一个方法,并在完成后得到通知,可以这样写


    dispatch_group_t serviceGroup = dispatch_group_create();
    dispatch_queue_t serviceQueue = dispatch_queue_create("com.serviceQueue.queue", NULL);
    
    for (NSUInteger i = 0; i < TASKS_COUNT; ++i) {
        dispatch_group_async(serviceGroup, serviceQueue, ^{
            [object perfromTask];
        });
    }
    
    // 亦可用wait代替
    dispatch_group_notify(serviceGroup, serviceQueue, ^{
        NSLog(@"Test 3 All tasks done dispatch_group_notify");
    });

中间部分也可使用enter和leave代替:

    for (NSUInteger i = 0; i < TASKS_COUNT; ++i) {
        dispatch_group_enter(serviceGroup);
        dispatch_async(serviceQueue, ^{
            [object perfromTask];
        });
    }

上面基本介绍了Dispatch Group的使用方法,然而这其中还隐藏着一个坑

Dispatch Group适用补充说明

这一句[object perfromTask];,实际使用中发现performTask异步还是同步对结果有一定发现,仔细验证发现是

 void dispatch_group_async(
   dispatch_group_t group,
   dispatch_queue_t queue,
   dispatch_block_t block);

void dispatch_group_enter(dispatch_group_t group);
void dispatch_group_leave(dispatch_group_t group);

并不完全等价。
dispatch_group_async中执行的block为异步时没法得到正确的通知
先上代码,block为同步的情况

    for (NSUInteger i = 0; i < TASKS_COUNT; ++i) {
        dispatch_group_async(serviceGroup, serviceQueue, ^{
            [self syncTaskWithNumber:i andCompletionBlock:^{
                NSLog(@"Test 4 sync End of task %ld", i);
            }];
        });
    }
    dispatch_group_notify(serviceGroup, serviceQueue, ^{
        NSLog(@"Test 4 sync All tasks done dispatch_group_notify");
    });

结果如下

2016-01-28 16:07:17.204 DispatchGroup[10126:952831] Test 4 sync End of task 0
2016-01-28 16:07:17.205 DispatchGroup[10126:952831] Test 4 sync End of task 1
2016-01-28 16:07:17.205 DispatchGroup[10126:952831] Test 4 sync End of task 2
2016-01-28 16:07:17.205 DispatchGroup[10126:952831] Test 4 sync End of task 3
2016-01-28 16:07:17.206 DispatchGroup[10126:952831] Test 4 sync End of task 4
2016-01-28 16:07:17.206 DispatchGroup[10126:952831] Test 4 sync All tasks done dispatch_group_notify

结果如上所示,完全正常,那么再看看异步block的情况

for (NSUInteger i = 0; i < TASKS_COUNT; ++i) {
        dispatch_group_async(serviceGroup, serviceQueue, ^{
            [self asyncTaskWithNumber:i andCompletionBlock:^{
                NSLog(@"Test 2 async End of task %ld", i);
            }];
        });
    }
dispatch_group_notify(serviceGroup, serviceQueue, ^{
        NSLog(@"Test 2 async All tasks done dispatch_group_notify");
    });

结果如下,可以发现dispatch_group_notify中的block被提前执行了

2016-01-28 16:10:08.403 DispatchGroup[10234:963964] Test 2 async End of task 0
2016-01-28 16:10:08.403 DispatchGroup[10234:963898] Test 2 async All tasks done dispatch_group_notify
2016-01-28 16:10:08.403 DispatchGroup[10234:964053] Test 2 async End of task 1
2016-01-28 16:10:08.403 DispatchGroup[10234:964054] Test 2 async End of task 2
2016-01-28 16:10:08.404 DispatchGroup[10234:964055] Test 2 async End of task 3
2016-01-28 16:10:08.404 DispatchGroup[10234:963964] Test 2 async End of task 4

那这是不是说Dispatch Group不适用异步block情况呢?请继续看dispatch_group_enter的表示

    for (NSUInteger i = 0; i < TASKS_COUNT; ++i) {
        dispatch_group_enter(serviceGroup);
        dispatch_async(serviceQueue, ^{
            [self asyncTaskWithNumber:i andCompletionBlock:^{
                NSLog(@"Test 5 End of task %ld", i);
                dispatch_group_leave(serviceGroup);
            }];
        });
    }

    dispatch_group_notify(serviceGroup, serviceQueue, ^{
        NSLog(@"Test 5 All tasks done dispatch_group_notify");
    });

结果如下:

2016-01-28 16:14:24.815 DispatchGroup[10342:981474] Test 5 End of task 2
2016-01-28 16:14:24.815 DispatchGroup[10342:981440] Test 5 End of task 1
2016-01-28 16:14:24.815 DispatchGroup[10342:981438] Test 5 End of task 0
2016-01-28 16:14:24.815 DispatchGroup[10342:981316] Test 5 End of task 3
2016-01-28 16:14:24.815 DispatchGroup[10342:981475] Test 5 End of task 4
2016-01-28 16:14:24.816 DispatchGroup[10342:981316] Test 5 All tasks done dispatch_group_notify

完全OK,也即是说异步情况只能用

void dispatch_group_enter(dispatch_group_t group);
void dispatch_group_leave(dispatch_group_t group);

总结

Dispatch Group可以用于管理过个任务,可以用dispatch_group_asyncdispatch_group_enter dispatch_group_enter两种方式将任务添加到Dispatch Group中,并且可以在在任务执行完成后得到通知,执行dispatch_group_notify中的block,亦可用dispatch_group_wait阻塞等待任务执行完成.

但是当任务需要异步执行的时候,dispatch_group_enter dispatch_group_enter才能确保dispatch_group_notify是在正确的时候(即Dispatch Group中的任务全部执行完毕)得到通知().

附注:asyncTaskWithNumbersyncTaskWithNumber的代码

- (void)asyncTaskWithNumber:(NSUInteger)number andCompletionBlock:(void (^)(void))completionBlock
{
    usleep(arc4random_uniform(SLEEP_TIME));
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        usleep(arc4random_uniform(SLEEP_TIME));
        completionBlock();
    });
}

- (void)syncTaskWithNumber:(NSUInteger)number andCompletionBlock:(void (^)(void))completionBlock
{
    usleep(arc4random_uniform(SLEEP_TIME));
    
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        usleep(arc4random_uniform(SLEEP_TIME));
        completionBlock();
    });
}

谢谢阅读。

___
我是 Wythe,iOS 开发者,对其他技术也有好奇。公众号 WytheTalk,从一个程序员的角度看世界,主要是技术分享,也有对互联网各种事的观点。欢迎关注。


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

推荐阅读更多精彩内容

  • Managing Units of Work(管理工作单位) 调度块允许您直接配置队列中各个工作单元的属性。它们还...
    edison0428阅读 7,948评论 0 1
  • NSThread 第一种:通过NSThread的对象方法 NSThread *thread = [[NSThrea...
    攻城狮GG阅读 795评论 0 3
  • 背景 担心了两周的我终于轮到去医院做胃镜检查了!去的时候我都想好了最坏的可能(胃癌),之前在网上查的症状都很相似。...
    Dely阅读 9,229评论 21 42
  • 3.GCD GCD的全称是Grand Central Dispatch,提供了非常多的纯C语言的函数 GCD的优势...
    Mario_ZJ阅读 476评论 0 0
  • 在这两部分的系列中,第一个部分的将解释 GCD 是做什么的,并从许多基本的 GCD 函数中找出几个来展示。在第二部...
    透支未来阅读 344评论 0 1