美文网首页
Dispatch Group总结

Dispatch Group总结

作者: Wythe | 来源:发表于2017-03-01 15:00 被阅读90次

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

    前段时间看叶孤城开源的下厨房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](https://img.haomeiwen.com/i446839/6e1ec13cf518d34c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    

    相关文章

      网友评论

          本文标题:Dispatch Group总结

          本文链接:https://www.haomeiwen.com/subject/fhaqgttx.html