dispatch_group的例子分析

函数解释:
1、dispatch groupGCD的一项特性,可以把任务分组。这组任务完成后时,调用者会收到通知

据此,可将要并发执行的多个任务合并为一组,这样调用者就可以知道这些任务何时能全部执行完

2、创建dispatch group:

dispatch_group_t dispatchGroup = dispatch_group_create();

3、将任务分组的两种方式:
方式一、用dispatch_group_async:

void dipatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
//比dispatch_async多了group分组这个参数

方式二、用dispatch_group_enter和dispatch_group_leave:

void dispatch_group_enter(dispatch_group_t group); //使分组里正要执行的任务数递增
void dispatch_group_leave(dispatch_group_t group); //使分组里的任务数递减

dispatch_group_enter和dispatch_group_leave就相当于引用计数里的保留和释放操作,必须搭配使用,以防内存泄漏

4、dispatch_group_wait:等待dispatch group执行完毕,会阻塞当前线程

void dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
//第一个参数是要等待的组
//第二个参数是等待时间。group执行时间不超过timeout返回0,超时返回非0值。一般用DISPATCH_TIME_FOREVER一直等待

5、dispatch_group_notify:等待dispatch group执行完之后执行块中的任务,不会阻塞当前线程

void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
//第一个参数是要等待的group
//第二个参数是block要在哪个队列中执行
//第三个参数是等待执行的代码块。将group执行完后需要处理的任务放block中

6、dispatch_apply:需要反复执行某个任务时使用,会阻塞当前线程
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block) (size_t) );
//iterations表示要执行的次数
//queue可以使用并发队列,这样系统会根据资源情况来并发执行

关于dispatch_group的概念以及几种场景下的使用

1、关于dispatch_group

把一组任务提交到队列中,这些队列可以不相关,然后监听这组任务完成的事件。
最常见的几个方法:

1、dispatch_group_create创建一个调度任务组
2、dispatch_group_async 把一个任务异步提交到任务组里
3、dispatch_group_enter/dispatch_group_leave 这种方式用在不使用dispatch_group_async来提交任务,且必须配合使用
4、dispatch_group_notify 用来监听任务组事件的执行完毕
5、dispatch_group_wait 设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败

2、实际使用场景

场景1:
现在有4个任务,任务1、任务2、任务3、任务4. 任务3必须在任务2之后,任务4必须在前3个任务都执行完成后,才能执行,并且需要在主线程更新UI。

思路分析:
任务3必须在任务2之后,所以这两个必须串行执行,同时,任务2和3整体可以和任务1并行执行,最后,任务4只能等待前3个任务全部执行完成,才能执行。这里就可以用group快速实现场景需求。

代码实现:

-(void)disGroup{
    dispatch_queue_t globalQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t selfQuene = dispatch_queue_create("myQuene", 0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQuene, ^{
        NSLog(@"run task 1");
    });
    dispatch_group_async(group, selfQuene, ^{
        NSLog(@"run task 2");
    });
    dispatch_group_async(group, selfQuene, ^{
        NSLog(@"run task 3");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"run task 4");
    });
}

注意: dispatch_queue_t selfQueue = dispatch_queue_create("myQueue",0) 这边的0参数等价于NULL,当参数为NULL的时候,就是创建一个串行队列。
为什么相当于NULL,因为在c语言中 给指针赋值为0的话,就相当于空指针

dispatch_get_global_queue 这里边的参数,第一个:是优先级 第二个:标记是为了未来使用保留的!所以这个参数应该永远指定为0

执行结果如下:(结果1和结果2是一样的,只是1和2的顺序区别)
结果1


image.png


结果2


image.png

总结: 1和(2、3)是并行执行关系,2、3是串行执行关系,且3肯定在2之后,而4在(1、2、3)全部完成之后才会执行。
2、dispatch_group_enter(group)、dispatch_group_leave(group) 何时使用

理论上讲,这两个方法其实就是:手动管理group关联的block的运行状态(或计数),并且使用时必须保证进入和退出group次数匹配。

所以:A和B两种使用方式可以讲是等价的

A)
dispatch_group_async(group, queue, ^{
  // 。。。
});
 
B)
dispatch_group_enter(group);
dispatch_async(queue, ^{
  //。。。
  dispatch_group_leave(group);
});

所以,这种用法和直接使用dispatch_group_notify一定程度上是等价的,大家可以自己选择使用。
场景2:
有3个异步请求任务,任务1、2、3,在3个任务全部完成之后,需要执行任务4,用以显示界面数据。
用 dispatch_group_enter、 dispatch_group_leave 实现:

-(void)disGroupEnterAndLeave{
    dispatch_queue_t globalQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
     
    //任务1
    dispatch_group_enter(group);
    dispatch_async(globalQuene, ^{
         NSLog(@"run task 1");
        sleep(1);
        dispatch_group_leave(group);
    });
     
    //任务2
    dispatch_group_enter(group);
    dispatch_async(globalQuene, ^{
        NSLog(@"run task 2");
        sleep(2);
        dispatch_group_leave(group);
    });
     
    //任务3
    dispatch_group_enter(group);
    dispatch_async(globalQuene, ^{
        NSLog(@"run task 3");
        sleep(3);
        dispatch_group_leave(group);
    });
     
    //一直等待完成
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
   
    //任务3
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"run task 4");
    });
     
}

执行结果:


image.png

3个任务是并行执行,且一共花费3s执行,然后才执行任务4。

当然,如果这样想串行执行3个任务,只需要把 全局队列 换成 自定义队列 即可。

注意事项:

/** 
 *  使用dispatch_group,异步多请求 
 */  
- (void)asyncBaseData  
{  
    // 全局并行队列  
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);  
    // 创建一个group  
    dispatch_group_t group = dispatch_group_create();  
      
    dispatch_group_async(group, globalQueue, ^{  
        // 执行请求1... (这里的代码需要时同步执行才能达到效果)  
    });  
      
      
    dispatch_group_async(group, globalQueue, ^{  
        // 执行请求2...  
    });  
  
    dispatch_group_async(group, globalQueue, ^{  
        // 执行请求N...  
    });  
      
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
        NSLog(@"全部请求执行完毕!");  
    });  
}  

当dispatch_group_async的block里面执行的是异步任务,如果还是使用上面的方法你会发现异步任务还没跑完就已经进入到了dispatch_group_notify方法里面了,这时用到
dispatch_group_enter和dispatch_group_leave就可以解决这个问题:

// 全局变量group  
    group = dispatch_group_create();  
    // 并行队列  
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
      
    // 进入组(进入组和离开组必须成对出现, 否则会造成死锁)  
    dispatch_group_enter(group);  
    dispatch_group_async(group, queue, ^{  
        // 执行异步任务1  
        [self fetchBaseData];  
    });  
      
    // 进入组  
    dispatch_group_enter(group);  
    dispatch_group_async(group, queue, ^{  
        // 执行异步任务2  
        [self fetchInspectorBaseData];  
    });  
      
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
        [SVProgressHUD dismiss];  
        ILog(@"全部基础数据下载完毕!");  
        [[AppDelegate sharedDelegate] showMainView];  
    });  
}  
  
#pragma mark - 获取基础数据  
- (void)fetchBaseData  
{  
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{  
        [SVProgressHUD showWithStatus:@"下载基础数据中..."];  
    });  
    NSDictionary *params = @{ kPage: @0, kPageSize: @9999 };  
    [BaseDataService fetchBaseDataWithParams:params showHUD:NO success:^(NSDictionary *response) {  
          
        if ([response[kStatusCode] intValue] == kSuccessCode) {  
              
            NSArray *array = [BaseDataModel arrayOfModelsFromDictionaries:response[@"rows"] error:nil];  
            if (!array || !array.count ) {  
                [SVProgressHUD showErrorWithStatus:@"下载基础数据失败"];  
                return;  
            }  
            // 保存数据库  
            [BaseDataService saveBaseData:array];  
            // 离开组  
            dispatch_group_leave(group);  
        }  
          
    } failure:^(NSError *error) {  
          
    }];  
}  
  
#pragma mark - 获取巡查基础数据  
- (void)fetchInspectorBaseData  
{  
    NSDictionary *params = @{ kPage: @0, kPageSize: @9999 };  
    [BaseDataService fetchInspectorBaseDataWithParams:params showHUD:NO success:^(NSDictionary *response) {  
        [SVProgressHUD dismiss];  
          
        if ([response[kStatusCode] intValue] == kSuccessCode) {  
              
            NSArray *array = [InspectorBaseDataModel arrayOfModelsFromDictionaries:response[@"rows"] error:nil];  
            if (!array || !array.count ) {  
                [SVProgressHUD showErrorWithStatus:@"下载巡查基础数据失败"];  
                return;  
            }  
            // 保存数据库  
            [BaseDataService saveInspectorBaseData:array];  
            // 离开组  
            dispatch_group_leave(group);  
        }  
          
    } failure:^(NSError *error) {  
          
    }];  
}  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容