GCD系列:隊列(queue)


GCD是基於C封裝的函數,具備非常高的效率,在ARC環境下,無須主動管理內存,無須dispatch_retain和dispatch_release,可以將重點關注在業務邏輯上。
GCD是基於隊列的封裝,下面淺要解析GCD的隊列。

GCD獲取線程的方式

  • void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);獲取一個異步線程隊列,queue用於指定block執行體所在的隊列
  • void dispatch_sync_f(dispatch_queue_t queue,void *_Nullable contex,dispatch_function_t work); 跟dispatch_sync類似,只不過接收的是一個dispatch_function_t的函數。
  • void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); 獲取一個異步線程,接收一個閉包block.
  • void dispatch_async_f(dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);獲取一個異步線程,接收一個函數指針.

GCD獲取隊列的方式

  • dispatch_queue_t dispatch_get_main_queue() //獲取主隊列
  • dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags); //獲取全局隊列,由系統分配,分配完成后不可更改,flags是預留字段,傳遞任何非0值將返回一個NULL值引發異常,identifier指定全局隊列的級別,隊列的級別如下:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

推薦使用基於QOS_CLASS的屬性級別.

QOS_CLASS_USER_INTERACTIVE  //最高優先級,用於UI更新等與用戶交互的操作.
QOS_CLASS_USER_INITIATED        //初始化優先級,用於初始化等,等同於DISPATCH_QUEUE_PRIORITY_HIGH
QOS_CLASS_DEFAULT                   //默認優先級,等同於DISPATCH_QUEUE_PRIORITY_DEFAULT
QOS_CLASS_UTILITY                     //低優先級,等同於DISPATCH_QUEUE_PRIORITY_LOW
QOS_CLASS_BACKGROUND         //后台級,用戶用戶無法感知的一些數據處理,等同於DISPATCH_QUEUE_PRIORITY_BACKGROUND
  • 自己創建的隊列.dispatch_queue_t dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);label表示該隊列的唯一標識字符串,可以使用const char *dispatch_queue_get_label(dispatch_queue_t _Nullable queue);來獲取該字符串,參數二attr指定隊列的執行順序,有以下參數:
DISPATCH_QUEUE_SERIAL                 //指定串行(FIFO)隊列,等同於傳入參數NULL
DISPATCH_QUEUE_CONCURRENT    //指定並發隊列,
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t _Nullable attr,dispatch_qos_class_t qos_class, int relative_priority);產生一個基於QOS_CLASS的隊列.

dispatch_apply應用

dispatch_apply必須要結合dispatch_async 或者dispatch_async_f函數一起使用,如果脫離了dispatch_async函數,程序很容易crash,需要特別關注.
在指定的queue中去直接執行dispatch_appl(count,queue,block);會直接引發crash!

  • void dispatch_apply(size_t iterations, dispatch_queue_t queue,DISPATCH_NOESCAPE void (^block)(size_t));
    應用一個block,執行block代碼塊iterations次,每次執行的index通過size_t參數傳遞到block代碼塊內部
    queue:指定apply函數接收的閉包block執行對應的隊列方式,如果是串行隊列,跟for循環功能一致,無法達到優化性能的目的。
    如果是並行隊列,則重復執行block的順序不定,以達到優化性能的目的,下面是2個簡單的例子:

    case 1:

void dispatchApply() {
	
	dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

	dispatch_async(dispatch_get_main_queue(), ^{
		
		//執行的順序取決於queue是串行還是並行,如果使用串行就跟for循環一樣,沒有意義
		//Dispatch_apply函數主要的功能是提高性能.
		dispatch_apply(10, queue, ^(size_t index) {
			
			NSLog(@"index....%lu",index);
		});
		//dispatch_apply是串行執行,知道10次invoke complete的時候,才繼續往下執行.
		NSLog(@"ddddd");
	});
}
輸出結果:  **可以看出,是並行執行的,達到了apply優化性能的目的.**
2017-02-27 14:56:03.856182 dispatch_queue[3154:130315] index....0
2017-02-27 14:56:03.856182 dispatch_queue[3154:130336] index....2
2017-02-27 14:56:03.856205 dispatch_queue[3154:130337] index....1
2017-02-27 14:56:03.856240 dispatch_queue[3154:130315] index....4
2017-02-27 14:56:03.856251 dispatch_queue[3154:130336] index....5
2017-02-27 14:56:03.856208 dispatch_queue[3154:130335] index....3
2017-02-27 14:56:03.856272 dispatch_queue[3154:130315] index....6
2017-02-27 14:56:03.856278 dispatch_queue[3154:130336] index....8
2017-02-27 14:56:03.856280 dispatch_queue[3154:130337] index....7
2017-02-27 14:56:03.856293 dispatch_queue[3154:130335] index....9
2017-02-27 14:56:03.856327 dispatch_queue[3154:130315] ddddd
####case 2:

```
void dispatchApplySerial() {

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_async(dispatch_get_global_queue(0, 0), ^{
	
	//執行的順序取決於queue是串行還是並行,如果使用串行就跟for循環一樣,沒有意義
	//Dispatch_apply函數主要的功能是提高性能.
	dispatch_apply(10, queue, ^(size_t index) {
		
		NSLog(@"index....%lu",index);
	});
	//dispatch_apply是串行執行,知道10次invoke complete的時候,才繼續往下執行.
	NSLog(@"ddddd");
});
}

輸出結果:  

2017-02-27 14:53:40.472788 dispatch_queue[3096:128184] index....0
2017-02-27 14:53:40.472830 dispatch_queue[3096:128184] index....1
2017-02-27 14:53:40.472842 dispatch_queue[3096:128184] index....2
2017-02-27 14:53:40.472851 dispatch_queue[3096:128184] index....3
2017-02-27 14:53:40.472860 dispatch_queue[3096:128184] index....4
2017-02-27 14:53:40.472868 dispatch_queue[3096:128184] index....5
2017-02-27 14:53:40.472877 dispatch_queue[3096:128184] index....6
2017-02-27 14:53:40.472885 dispatch_queue[3096:128184] index....7
2017-02-27 14:53:40.472893 dispatch_queue[3096:128184] index....8
2017-02-27 14:53:40.472902 dispatch_queue[3096:128184] index....9
2017-02-27 14:53:40.472931 dispatch_queue[3096:128223] ddddddddd
```

  • void dispatch_apply_f(size_t iterations, dispatch_queue_t queue,void _Nullable context,void (work)(void *_Nullable, size_t));
    跟dispatch_apply功能一致,方法接收一個函數指針.

void dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t _Nullable queue);

dispatch_set_target_queue可以將object指向的dispatch_object_t對象的隊列方式按照參數2的queue的隊列方式去執行,它的一大功能就是可以把並發的函數變為串行執行,下面是例子:

void setTargetQueue() {
	
	dispatch_queue_t _serialQueue = dispatch_queue_create("this.is.serial.queue", DISPATCH_QUEUE_SERIAL);
	dispatch_queue_t _concurrcyQueue = dispatch_queue_create("this.is.concurrency.queue", DISPATCH_QUEUE_CONCURRENT);
//	dispatch_set_target_queue(_concurrcyQueue,_serialQueue);
	NSInteger index = 0;
	while (index ++ < 5) {
		
		dispatch_async(_concurrcyQueue, ^{ NSLog(@"11111111111"); });
		dispatch_async(_concurrcyQueue, ^{ NSLog(@"22222222222"); });
		dispatch_async(_serialQueue, ^{	NSLog(@"3333333333"); });
	}
}
//執行的結果如下:
2017-02-27 15:22:42.853347 dispatch_queue[3443:148056] 3333333333
2017-02-27 15:22:42.853346 dispatch_queue[3443:148077] 11111111111
2017-02-27 15:22:42.853367 dispatch_queue[3443:148069] 11111111111
2017-02-27 15:22:42.853375 dispatch_queue[3443:148057] 22222222222
2017-02-27 15:22:42.853437 dispatch_queue[3443:148056] 3333333333
2017-02-27 15:22:42.853475 dispatch_queue[3443:148077] 22222222222
2017-02-27 15:22:42.853482 dispatch_queue[3443:148069] 11111111111
2017-02-27 15:22:42.853499 dispatch_queue[3443:148057] 22222222222
2017-02-27 15:22:42.853507 dispatch_queue[3443:148056] 3333333333
2017-02-27 15:22:42.853519 dispatch_queue[3443:148077] 11111111111
2017-02-27 15:22:42.853529 dispatch_queue[3443:148069] 22222222222
2017-02-27 15:22:42.853538 dispatch_queue[3443:148057] 11111111111
2017-02-27 15:22:42.853546 dispatch_queue[3443:148056] 3333333333
2017-02-27 15:22:42.853557 dispatch_queue[3443:148077] 22222222222
2017-02-27 15:22:42.853585 dispatch_queue[3443:148056] 3333333333
//可以看出,執行結果很混亂,屬於並發執行,現在打開set_target_queue注釋,得到以下結果:
2017-02-27 15:25:06.510355 dispatch_queue[3470:149395] 11111111111
2017-02-27 15:25:06.510405 dispatch_queue[3470:149395] 22222222222
2017-02-27 15:25:06.510423 dispatch_queue[3470:149395] 3333333333
2017-02-27 15:25:06.510438 dispatch_queue[3470:149395] 11111111111
2017-02-27 15:25:06.510452 dispatch_queue[3470:149395] 22222222222
2017-02-27 15:25:06.510465 dispatch_queue[3470:149395] 3333333333
2017-02-27 15:25:06.510477 dispatch_queue[3470:149395] 11111111111
2017-02-27 15:25:06.510491 dispatch_queue[3470:149395] 22222222222
2017-02-27 15:25:06.510501 dispatch_queue[3470:149395] 3333333333
2017-02-27 15:25:06.510512 dispatch_queue[3470:149395] 11111111111
2017-02-27 15:25:06.510524 dispatch_queue[3470:149395] 22222222222
2017-02-27 15:25:06.510536 dispatch_queue[3470:149395] 3333333333
2017-02-27 15:25:06.510548 dispatch_queue[3470:149395] 11111111111
2017-02-27 15:25:06.510560 dispatch_queue[3470:149395] 22222222222
2017-02-27 15:25:06.510575 dispatch_queue[3470:149395] 3333333333
這就是典型的dispatch_set_target並發變FIFO串行執行功能.

延時函數dispatch_after

  • void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
    1. 參數1 when指定block執行的時間,
    2. 參數2 queue指定block執行的隊列形式,
    3. 參數3 block指定延時函數接收的閉包.
  • void dispatch_after_f(dispatch_time_t when,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
    1.跟dispatch_after功能一樣.
    2.參數3 work指定延時函數接收一個函數指針.

Dispatch_barrier

dispatch_barrier在多線程編程中用來保證某個值域的原子性。在多線程操作中,同時對於同一個值的讀取不會有問題,但如果同時對一個值進行修改就會產生沖突,此時dispatch_barrier可以很好的解決這個問題,dispatch_barrier就像一個盒子,當盒子內的任務沒有出來前,盒子外的任務全部維護到一個隊列中。
相關函數如下:

  • void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);//將閉包放入同步環境的queue隊列中執行.
  • void dispatch_barrier_sync_f(dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);//將函數放入同步環境中的queue執行
  • void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);//將閉包放入異步環境的queue隊列中執行.
  • void dispatch_barrier_async_f(dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);//將函數放入異步環境中的queue執行
    事例代碼如下:
NSMutableDictionary *_vars;
void setVars(NSMutableDictionary *vars) {
	
	dispatch_queue_t _serialQueue = dispatch_queue_create("this.is.serial.queue", DISPATCH_QUEUE_SERIAL);
	dispatch_barrier_async(_serialQueue, ^{
		
		_vars = vars;
	});
}

為一個隊列添加屬性和獲取屬性

GCD允許給一個隊列通過特定的key值關聯屬性contenxt,有點類似於使用runtime的objc_associated,在類別中給一個類添加屬性,用於實際業務需要.
當key對應的context發生變化時,會觸發C函數destructor.

  • void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key,void *_Nullable context, dispatch_function_t _Nullable destructor);
    //通過key,為一個queue設置context
  • void *_Nullable dispatch_queue_get_specific(dispatch_queue_t queue, const void *key);
    //通過key,從一個queue讀取context
  • void *_Nullable dispatch_get_specific(const void *key);
    //測試當前隊列是否是key對應的queue隊列(有待認證。。)
    示例代碼如下:
void destructorInvoke(const void *string) {
	
	NSLog(@"destructor ----->%@",[[NSString alloc] initWithUTF8String:(char *)string]);
}
dispatch_queue_t _serialQueue;
void dispatchSpecific() {
	
	setSpecific(@"1");
	setSpecific(@"2");
	setSpecific(@"3");
	setSpecific(@"4");
	setSpecific(@"5");
}
void setSpecific(NSString *context) {
	
	if (!_serialQueue) {
		
		_serialQueue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);
	}
	const char *key = "set one context";
	NSLog(@"set string:%@",context);
	dispatch_queue_set_specific(_serialQueue, key, context.UTF8String, &destructorInvoke);
	
	NSLog(@"context is : %@",[NSString stringWithUTF8String:dispatch_queue_get_specific(_serialQueue,key)]);
}
輸出結果:
2017-02-27 16:14:25.026095 dispatch_queue[3855:177340] set string:1
2017-02-27 16:14:25.026151 dispatch_queue[3855:177340] context is : 1
2017-02-27 16:14:25.026166 dispatch_queue[3855:177340] set string:2
2017-02-27 16:14:25.026194 dispatch_queue[3855:177340] context is : 2
2017-02-27 16:14:25.026206 dispatch_queue[3855:177340] set string:3
2017-02-27 16:14:25.026212 dispatch_queue[3855:177396] destructor ----->1
2017-02-27 16:14:25.026225 dispatch_queue[3855:177340] context is : 3
2017-02-27 16:14:25.026228 dispatch_queue[3855:177396] destructor ----->2
2017-02-27 16:14:25.026241 dispatch_queue[3855:177340] set string:4
2017-02-27 16:14:25.026298 dispatch_queue[3855:177340] context is : 4
2017-02-27 16:14:25.026307 dispatch_queue[3855:177396] destructor ----->3
2017-02-27 16:14:25.026315 dispatch_queue[3855:177340] set string:5
2017-02-27 16:14:25.026335 dispatch_queue[3855:177340] context is : 5
2017-02-27 16:14:25.026338 dispatch_queue[3855:177396] destructor ----->4

博主已經開通了博客地址: kobeluo,哪里有更豐富的資源,歡迎與我交流。


免責聲明!

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



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