蘋果官方說明,GCD是異步執行任務的技術之一,通常將應用程序中記述的線程管理用的代碼在系統級中實現。開發者只須要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。因爲線程管理是做爲系統的一部分來實現的,所以能夠統一管理,也能夠執行任務。以下:數據庫
//串形隊列 第二個參數最好爲 NULL 參考源碼編程
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.mySerialQueue", NULL);
//並行隊列 第二個參數最好爲 DISPATCH_QUEUE_CONCURRENT數組
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.gcd.myCurrentQueue", DISPATCH_QUEUE_CONCURRENT);安全
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 長時間處理 如:數據庫訪問 dispatch_async(dispatch_get_main_queue(), ^{ //主線程更新 }); });
其實只有第一行代碼表示讓處理在後臺執行。也只有一行代碼讓處理在主線程中執行。多線程
在GCD以前,Cocoa框架也提供NSObject類來實現一樣的效果:併發
//執行後臺線程 [self performSelectorInBackground:@selector(doWork) withObject:nil];
- (void)doWork{ // 長時間處理 如:數據庫訪問 // ........................... //處理結束 ,主線城處理結果 [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO]; } - (void)doneWork{ //主線程執行 如用戶界面更新 }
這個方法的確要比NSThread提供的簡單,可是相比於GCD仍是繁瑣了。在多線程編程中,因爲CPU一次只能執行一個命令,GCD的優點就更加明顯。app
------Dispatch Queue的種類框架
//獲取主線程 屬於serial queue dispatch_queue_t mainQueue = dispatch_get_main_queue(); //高優先級獲取方法 屬於concurrent queue dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); //默認先級獲取方法 屬於concurrent queue dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //低優先級獲取方法 屬於concurrent queue dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //後臺優先級獲取方法 屬於concurrent queue dispatch_queue_t globalDispatchQueueBg = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);--
-----指定優先級 ,若是在多個serial dispatch queue中用此函數制定目標爲某一個serial dispatch queue,那麼本來能夠併發執行的多個serial dispatch queue,在目標serial dispatch queue上只能同時執行一個處理。異步
//指定變動優先級 第一個參數若是是系統提供的如main dispatch queue 和 global dispatch queue 均不可指定 //第一個參數指定變動優先級 第二個參數指定與要使用的執行的相同優先級 dispatch_set_target_queue(globalDispatchQueueHigh, globalDispatchQueueLow);
-----Dispatch Groupasync
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t groupQueue = dispatch_group_create(); //這五個block併發執行 dispatch_group_async(groupQueue, queue, ^{NSLog(@"block0");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block1");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block2");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block3");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block4");}); //結束處理 必定是最後執行的 dispatch_group_notify(groupQueue, queue, ^{NSLog(@"done");});
dispatch_group中也可使用dispatch_wait函數等待所有處理執行結束
// DISPATCH_TIME_FOREVER 永久等待,group未結束就會一直等待 dispatch_group_wait(groupQueue, DISPATCH_TIME_FOREVER); //如: dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC); long result = dispatch_group_wait(groupQueue, time); if (result == 0) { //若是不爲0,就說明雖然過了等待時間,可是某一個group還在處理執行中。 //屬於dispatch group的所有處理執行結束 NSLog(@"屬於dispatch group的所有處理執行結束"); }else{ //屬於dispatch group的某一個處理還在執行中 NSLog(@"屬於dispatch group的某一個處理還在執行中"); }
還有常常碰到的等待多個異步請求執行完畢方法以下:
dispatch_group_t serviceGroup = dispatch_group_create(); //開始第一個請求 //進入組 dispatch_group_enter(serviceGroup); [self.configService startWithCompletion:^(ConfigResponse *resul ts, NSError* error){ configError = error; //離開組 dispatch_group_leave(serviceGroup); }]; //開始第二個請求 //進入組 dispatch_group_enter(serviceGroup); [self.preferenceService startWithCompletion:^(PreferenceRespons e *results, NSError* error){ //離開組 preferenceError = error; dispatch_group_leave(serviceGroup); }]; //當小組裏的任務都清空之後 通知主線程 dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{ // Assess any errors NSError *overallError = nil; if (configError || preferenceError) { // Either make a new error or assign one of them to the overall error overallError = configError ?: preferenceError; } // Now call the final completion block completion(overallError); });
----dispatch_barrier_async在訪問數據庫或文件時,使用serial dispatch queue 可避免數據競爭。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.barrier", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{NSLog(@"block0_for_reading");}); dispatch_async(queue, ^{NSLog(@"block1_for_reading");}); dispatch_async(queue, ^{NSLog(@"block2_for_reading");}); dispatch_async(queue, ^{NSLog(@"block3_for_reading");}); //須要寫入處理 -- 當前面執行完再執行dispatch_barrier中的處理,在執行後面的操做 dispatch_barrier_async(queue, ^{NSLog(@"blockBarrier_for_writing");}); dispatch_async(queue, ^{NSLog(@"block4_for_reading");}); dispatch_async(queue, ^{NSLog(@"block5_for_reading");}); dispatch_async(queue, ^{NSLog(@"block6_for_reading");}); dispatch_async(queue, ^{NSLog(@"block7_for_reading");}); dispatch_async(queue, ^{NSLog(@"block8_for_reading");});
----dispatch_apply 是dispatch_async和dispatch_group的關聯API。
按指定的次數將指定的block追加到指定的dispatch_queue中,並等待所有執行結束。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%zu",index); }); NSLog(@"done");
有這樣的功能,咱們就能夠很安全的去循環數組進行元素處理(注:不是按順序執行)。
NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(array.count, queue, ^(size_t index) { NSLog(@"%zu-%@",index,array[index]); }); NSLog(@"done");
另外,因爲dispatch_apply 和 diapatch_sync 函數相同。會等待處理執行結束,所以推薦在dispatch_async函數中非同步的執行dispatch_apply函數
NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //在global dispatch queue中非同步執行 dispatch_async(queue, ^{ //等待dispatch_apply函數中所有處理執行結束 dispatch_apply(array.count, queue, ^(size_t index) { //並列處理包含在NSArray對象的所有對象 NSLog(@"%zu-%@",index,[array objectAtIndex:index]); }); //dispatch_apply函數所有處理執行結束 dispatch_async(dispatch_get_main_queue(), ^{ //主線程中更新UI NSLog(@"主線程中更新UI"); }); });
----dispatch_suspend 掛起中止執行, dispatch_resume恢復執行
----dispatch_semaphore 是持有計數的信號,此計數是變線程編程中計數類型信號。相似於過馬路的手旗,能夠經過舉起手旗,不可經過放下手旗。dispatch_semaphore,計數爲0等待,計數大於或等於1時,減去1而不等待。
//建立dispatch_semaphore 計數值初始化爲1 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC); long result = dispatch_semaphore_wait(semaphore, time); if (result == 0) {//返回爲0的時候能夠安全的執行能夠進行排他控制的處理 /* 因爲dispatch_semaphore的計數值達到大於等於1 或者在待機中的指定時間內 dispatch_semaphore的計數值達到大於等於1 因此dispatch_semaphore的計數值減去1. 可執行須要進行排他控制處理 */ }else{ /* 因爲dispatch semaphore的計數值爲0 所以在達到指定時間爲止待機 */ }
實際使用:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //建立dispatch_semaphore 計數值初始化爲1 //保證訪問NSMutableArray類對象的線程同時只能有一個 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); NSMutableArray *array = [[NSMutableArray alloc]init]; for (int i = 0 ; i < 10000; i ++) { dispatch_async(queue, ^{ /* 等待dispatch_smaphore 一直等待,直到dispatch_semaphore的計數值達到大於或等於1 */ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); /* 因爲dispatch_semaphore的計數值達到大於等於1 因此將dispatch_semaphore的計數值減去1 dispatch_semaphore_wait函數執行返回 即此時的dispatch semaphore 恆爲0 因爲能夠訪問NSMutableArray類對象的線程只有1個 所以能夠安全的進行更新 */ [array addObject:[NSNumber numberWithInt:i]]; /* 排他控制處理結束,因此經過dispatch_semaphore_signal函數將dispatch_samaphore的計數值加1. 若是有經過dispatch_semaphore_wait函數等待dispatch_semaphore的計數值增長的線程,就由最早等待的線程執行。 */ dispatch_semaphore_signal(semaphore); }); }
在沒有serial dispatch queue和dispatch_barrier_async函數那麼大粒度且一部分處理須要進行排他處理的狀況下,diapatch_semaphore即可以發揮威力。
----Dispatch I/O
在讀取較大文件時,若是將文件分紅合適的大小並使用global dispatch queue並列讀取的話,會比通常的讀取快很多。如今的輸入/輸出硬件已經能夠作到一次使用多個線程更快的並列讀取了。能實現這個功能的只有Dispatch I/O 和 Dispatch Data,這個後面更新。