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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM