• GCD系列:调度组(dispatch_group)


    Dispatch_group

    GCD头文件group.h中谈到,可以将一组block提交到调度组(dispatch_group)中,执行逐个串行回调,下面来看看相关函数。


    函数申明与理解

    • dispatch_group_t dispatch_group_create(void);
      //创建一个调度组,释放调度组使用dispatch_release()函数,创建成功返回一个dispatch_group调度组,失败则返回NULL.

    • void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
      //提交一个闭包函数(block)到queue中,并关联到指定的group调度组.通过typedef void (^dispatch_block_t)(void);我们可以发现,该函数无法给block传递参数.
      1. group 指定的调度组,block的关联调度组。
      2. queue 提交闭包函数(block)的队列。
      3. block 提交到指定queue的闭包函数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调度组,函数返回void.
      1. group 指定的调度组,block的关联调度组。
      2. queue 提交闭包函数(block)的队列。
      3. context 传递到函数中的的参数。
      4. work 在指定的queue中的指定函数。

    • long dispatch_group_wait(dispatch_group_t group, 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. 给定的闭包函数.

    • 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.

    • void dispatch_group_enter(dispatch_group_t group);

    • void dispatch_group_leave(dispatch_group_t group);
      //这一对函数调用一次意味着非使用dispatch_group_async方式,将一个block提交到指定的queue上并关联到group调度组.两个函数必须成对出现。


    实际例子

    • 环境变量与函数
    //create one group.
    dispatch_group_t _group;
    dispatch_queue_t _serialQ;
    void dispatch_group_test() {
    	
    	_group = dispatch_group_create();
    	_serialQ = dispatch_queue_create("this.is.a.serial.queue", DISPATCH_QUEUE_SERIAL);
    
        // one_test_function_use();
    }
    

    dispatch_group_test函数下面会继续提到,标记为“入口函数”

    定义一个调度组_group和一个串行队列_serialQ,下面所有的测试函数都有效.

    • dispatch_group_async函数的使用实例
    void dispatch_group_async_use() {
    
    	void (^noParameterHandle)(void) = ^(void) {
    		
    		NSLog(@"execute block");
    	};
    	
    	for (NSInteger index = 0; index < 5; index ++) {
    		
    		dispatch_group_async(_group, _serialQ,noParameterHandle);
    	}
    }
    
    执行结果:
    2017-03-01 17:33:23.809238 dispatch_data[17920:907260] execute block
    2017-03-01 17:33:23.809343 dispatch_data[17920:907260] execute block
    2017-03-01 17:33:23.809358 dispatch_data[17920:907260] execute block
    2017-03-01 17:33:23.809371 dispatch_data[17920:907260] execute block
    2017-03-01 17:33:23.809381 dispatch_data[17920:907260] execute block
    

    可以从实例中看出,使用dispatch_group_async函数关联的block无法传递参数.

    • dispatch_group_async_f函数实践示例

    先实现一个类型为void ()(void *)的C函数

    void function_t_use(void *value) {
    	
    	char *cString = value;
    	
    	NSLog(@"value is : %@",[NSString stringWithUTF8String:cString]);
    }      
    

    下面是测试函数⤵️

    void dispatch_group_async_f_use() {
    	
    	for (NSInteger index = 0; index < 5; index ++) {
    		
    		char *cString = (char *)[NSString stringWithFormat:@"%ld",index].UTF8String;
    		
    		dispatch_group_async_f(_group, _serialQ, cString, function_t_use);
    	}
    }
    执行结果:
    2017-03-01 18:00:39.641617 dispatch_data[18368:930399] value is : 0
    2017-03-01 18:00:39.641656 dispatch_data[18368:930399] value is : 1
    2017-03-01 18:00:39.641673 dispatch_data[18368:930399] value is : 2
    2017-03-01 18:00:39.641688 dispatch_data[18368:930399] value is : 3
    2017-03-01 18:00:39.641700 dispatch_data[18368:930399] value is : 4
    
    

    dispatch_group_async_f_use 允许传递参数到function中,需要注意的是传递的参数尽量使用char *,测试时使用int *不能正确的得到结果.

    • long dispatch_group_wait(group,timeout) 函数实践示例
      当group关联的block实行完毕时 long = 0 属于正常情况,
    void dispatch_group_waite_normal_use() {
    	
    	void (^noParameterHandle)(void) = ^(void) {
    		
    		NSLog(@"execute block");
    	};
    	
    	dispatch_group_async(_group, _serialQ, noParameterHandle);
    	
    	NSLog(@"will waite...");
    	
    	long count = dispatch_group_wait(_group, dispatch_time(DISPATCH_TIME_NOW,10 * NSEC_PER_SEC));
    	
    	NSLog(@"count: %ld",count);
    }
    返回结果:
    2017-03-01 18:21:39.823695 dispatch_data[18466:940513] will waite...
    2017-03-01 18:21:39.823699 dispatch_data[18466:940543] execute block
    2017-03-01 18:21:39.823762 dispatch_data[18466:940513] count: 0
    

    可以看出,block注册到了_group中,属于异步函数,当前线程继续向下执行,打印willwaite...,之后调用dispatch_group_waite函数进入等待,由于单次block回调非常快,不会超过timeout的时间,最终打印count = 0,当timeout超时group还有关联的任务时,将返回非0值错误。

    void dispatch_group_waite_timeout_use() {
    	
    	void (^noParameterHandle)(void) = ^(void) {
    		
    		sleep(1);
    		NSLog(@"execute block");
    	};
    	
    	for (NSUInteger index = 0; index < 5; index ++) {
    		
    		dispatch_group_async(_group, _serialQ, noParameterHandle);
    	}
    	
    	NSLog(@"will waite...");
    	
    	long count = dispatch_group_wait(_group, dispatch_time(DISPATCH_TIME_NOW,3 * NSEC_PER_SEC));
    	
    	NSLog(@"count: %ld",count);
    }
    执行结果:
    2017-03-01 18:31:02.762822 dispatch_data[18547:945276] will waite...
    2017-03-01 18:31:03.767823 dispatch_data[18547:945310] execute block
    2017-03-01 18:31:04.770983 dispatch_data[18547:945310] execute block
    2017-03-01 18:31:05.763463 dispatch_data[18547:945276] count: 49
    2017-03-01 18:31:05.772842 dispatch_data[18547:945310] execute block
    2017-03-01 18:31:06.777980 dispatch_data[18547:945310] execute block
    2017-03-01 18:31:07.779768 dispatch_data[18547:945310] execute block
    

    该测试函数指定dispatch_group_wait函数的timeout是3秒,在执行完两次for循环后已经超时,最后得到的count值非0 count=49.
    实际上该测试函数隐藏着一个值得讨论的地方:
    在noParameterHandle这个闭包函数中,直接使用sleep(1),闭包执行的队列是_serialQ,
    对于整个dispatch_group_waite_timeout_use函数,测试的时候是放在了mainQueue去执行,也就是dispatch_group_wait函数是在mainQueue中执行,此时跟闭包执行的队列不一致,各自在自己的队列执行,得到了上面的结果。

    假设整个dispatch_group_waite_timeout_use函数的执行体所在的队列就是_serialQ,而闭包所在的队列也是_serialQ,所以就相当于6个task都提交到了_serialQ,task1代表dispatch_group_waite_timeout_use函数,task2..6代表noParameterHandle(有一个for循环),由于是串行执行,当代码执行到dispatch_group_wait函数时,整个_serialQ将进入等待,3秒之后,打印count值,之后再串行执行5个闭包task.
    更换上面提到的“入口函数”,实践讨论的内容,代码如下:

    void dispatch_group_test() {
    	
    	_group = dispatch_group_create();
    	_serialQ = dispatch_queue_create("this.is.a.serial.queue", DISPATCH_QUEUE_SERIAL);
    	
    	dispatch_async(_serialQ, ^{
    		
    		dispatch_group_waite_timeout_use();
    	});
    }
    打印结果:
    2017-03-01 18:36:26.547770 dispatch_data[18637:949068] will waite...
    2017-03-01 18:36:29.549184 dispatch_data[18637:949068] count: 49
    2017-03-01 18:36:30.550143 dispatch_data[18637:949068] execute block
    2017-03-01 18:36:31.551091 dispatch_data[18637:949068] execute block
    2017-03-01 18:36:32.552570 dispatch_data[18637:949068] execute block
    2017-03-01 18:36:33.557777 dispatch_data[18637:949068] execute block
    2017-03-01 18:36:34.562879 dispatch_data[18637:949068] execute block
    
    
    • void dispatch_group_notify(group,queue,block);函数实践示例
      当group所关联的block全部执行结束时,会立马将给定block关联到group中,并在给定的queue中执行.代码如下:
    void dispatch_grout_notify_use() {
    	
    	void (^noParameterHandle)(void) = ^(void) {
    		
    		NSLog(@"execute block");
    	};
    	
    	for (NSUInteger index = 0; index < 5; index ++) {
    		
    		dispatch_group_async(_group, _serialQ, noParameterHandle);
    	}
    	
    	dispatch_group_notify(_group, _serialQ, ^{
    		
    		NSLog(@"executing the notify block...");
    	});
    }
    执行结果:
    2017-03-01 18:55:06.172181 dispatch_data[18706:955792] execute block
    2017-03-01 18:55:06.172216 dispatch_data[18706:955792] execute block
    2017-03-01 18:55:06.172229 dispatch_data[18706:955792] execute block
    2017-03-01 18:55:06.172240 dispatch_data[18706:955792] execute block
    2017-03-01 18:55:06.172249 dispatch_data[18706:955792] execute block
    2017-03-01 18:55:06.172262 dispatch_data[18706:955792] executing the notify block...
    

    由于dispatch_group_async是异步调用的,而dispatch_group_notify是同步调用的,所以,从该示例可以可以得到上述结论,dispatch_group_notify可以用于需要指定task之间的顺序时。

    • void dispatch_group_enter(group)与dispatch_group_leave(group);函数实践示例
      当你的代码无法使用dispatch_group_async函数去关联一个block到给定的调度组时,可以使用这对函数达到相同的功能.这里使用它来达到dispatch_group_async的功能,代码如下:
    void dispatch_group_leave_enter_use() {
    	
    	void (^noParameterHandle)(void) = ^(void) {
    		
    		NSLog(@"execute block");
    	};
    	
    	for (NSInteger index = 0; index < 5; index ++) {
    		
    		dispatch_group_enter(_group);
    		dispatch_async(_serialQ, noParameterHandle);
    		dispatch_group_leave(_group);
    		//enter 和 leave 必须成对出现,否则会引发crash.
    	}
    }
    执行结果:
    2017-03-01 19:02:53.657501 dispatch_data[18739:958841] execute block
    2017-03-01 19:02:53.657781 dispatch_data[18739:958841] execute block
    2017-03-01 19:02:53.657803 dispatch_data[18739:958841] execute block
    2017-03-01 19:02:53.657816 dispatch_data[18739:958841] execute block
    2017-03-01 19:02:53.657827 dispatch_data[18739:958841] execute block
    

    可以看出,当无法使用dispatch_group_async函数时,可以使用dispatch_group_enter和leave达到相同的效果.


    dispatch_group主题功能介绍完毕,水平有限,为不误人子弟,如有错误之处,还请各位大神一定指出,在此谢过。

    博主已经开通了博客地址: kobeluo,哪里有更丰富的资源,欢迎与我交流。

    搭建博客方法:https://hexo.io/ http://liuhongjiang.github.io/hexotech/2012/11/21/how-to-build-blog/

  • 相关阅读:
    select SCOPE_IDENTITY()用法
    SQL 2005 with(nolock)详解
    .NET4进行COM互操作导出数据到Excel
    Counterfeit Dollar 1013 pku
    Numbers that count 1016 PKU
    对局问题 ——取火柴问题(转)
    (a^b) mod c
    对局问题——放硬币问题(转)
    对局问题 ——取石子问题– 1堆(转)
    Follow My Logic 1048 PKU
  • 原文地址:https://www.cnblogs.com/KobeLuo/p/6484827.html
Copyright © 2020-2023  润新知