本文來自尚妝iOS團隊嘉文
發表於尚妝github博客,歡迎訂閱!git
Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法
基於C語言,提供了很是多強大的函數github
同步 (Synchronous)
在當前線程中執行任務,不具有開啓新線程的能力
提交的任務在執行完成後纔會返回
同步函數: dispatch_sync()objective-c
異步 (Asynchronous)
在新線程中執行任務,具有開啓新線程的能力
提交的任務馬上返回,在後臺隊列中執行
異步函數: dispatch_async()編程
串行 (Serial)
一個任務執行完畢後,再執行下一個任務安全
併發 (Concurrent)
多個任務同時執行(自動開啓多個線程),
只有在異步函數下才有效
併發
描述 | 說明 |
---|---|
queue | 隊列 |
main | 主隊列 |
global | 全局隊列 |
dispatch_queue_t | 描述隊列 |
dispatch_block_t | 描述任務 |
dispatch_once_t | 描述一次性 |
dispatch_time_t | 描述時間 |
dispatch_group_t | 描述隊列組 |
dispatch_semaphore_t | 描述信號量 |
函數 | 說明 |
---|---|
dispatch_sync() | 同步執行 |
dispatch_async() | 異步執行 |
dispatch_after() | 延時執行 |
dispatch_once() | 一次性執行 |
dispatch_apply() | 提交隊列 |
dispatch_queue_create() | 建立隊列 |
dispatch_group_create() | 建立隊列組 |
dispatch_group_async() | 提交任務到隊列組 |
dispatch_group_enter() / dispatch_group_leave() | 將隊列組中的任務未執行完畢的任務數目加減1(兩個函數要配合使用) |
dispatch_group_notify() | 監聽隊列組執行完畢 |
dispatch_group_wait() | 設置等待時間(返回 0成功,1失敗) |
1.全部的執行都放到隊列中(queue),隊列的特色是FIFO(先提交的先執行)app
2.必須在主線程訪問 UIKit 的類
3.併發隊列只在異步函數下才有效異步
NSLog(@"當前線程: %@", [NSThread currentThread]); //獲取主隊列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //獲取全局併發隊列 dispatch_queue_t otherQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //同步函數(在當前線程中執行,不具有開啓新線程的能力) dispatch_sync(otherQueue, ^{ NSLog(@"同步 %@", [NSThread currentThread]); }); //異步函數(在另外一條線程中執行,具有開啓新線程的能力) dispatch_async(otherQueue, ^{ NSLog(@"異步 %@", [NSThread currentThread]); }); //輸出: 當前線程: <NSThread: 0x7f81bac06670>{number = 1, name = main} //輸出: 同步 <NSThread: 0x7f81bac06670>{number = 1, name = main} //輸出: 異步 <NSThread: 0x7f81bae35be0>{number = 3, name = (null)}
dispatch_after()延遲一段時間把一項任務提交到隊列中執行,返回以後就不能取消
經常使用來在在主隊列上延遲執行一項任務async
NSLog(@"當前線程 %@", [NSThread currentThread]); //GCD延時調用(主線程)(主隊列) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"GCD延時(主線程) %@", [NSThread currentThread]); }); //GCD延時調用(其餘線程)(全局併發隊列) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"GCD延時(其餘線程) %@", [NSThread currentThread]); }); //輸出: 當前線程 <NSThread: 0x7f8cb9701990>{number = 1, name = main} //輸出: GCD延時(主線程) <NSThread: 0x7f8cb9701990>{number = 1, name = main} //輸出: GCD延時(其餘線程) <NSThread: 0x7f8cb9513ee0>{number = 3, name = (null)}
整個程序運行中,只會執行一次 (默認線程是安全的)
dispatch_once() 以線程安全的方式執行且僅執行其代碼塊一次函數
for (NSInteger i = 0; i < 10; i++) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"GCD一次性執行(默認線程是安全的)"); }); } //輸出: GCD一次性執行(默認線程是安全的)
//使用GCD初始化單例 + (instancetype)sharedManager { static PhotoManager *sharedPhotoManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedPhotoManager = [[PhotoManager alloc] init]; }); return sharedPhotoManager; }
把一項任務提交到隊列中屢次執行,具體是並行執行仍是串行執行由隊列自己決定
dispatch_apply不會馬上返回,在執行完畢後纔會返回,是同步的調用。
任務1,任務2依次執行,全部任務都執行成功後回到主線程
(效率不高)
NSLog(@"當前線程 %@", [NSThread currentThread]); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //全局併發隊列 for (NSInteger i = 0; i < 5; i++) { NSLog(@"任務1 %@", [NSThread currentThread]); } for (NSInteger i = 0; i < 5; i++) { NSLog(@"任務2 %@", [NSThread currentThread]); } dispatch_async(dispatch_get_main_queue(), ^{ //主隊列 NSLog(@"主線程執行(刷新UI) %@", [NSThread currentThread]); }); }); //輸出: 當前線程 <NSThread: 0x7fa78040b8b0>{number = 1, name = main} //輸出: 任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)} //輸出: 主線程(刷新UI) <NSThread: 0x7f9da1705940>{number = 1, name = main}
任務1,任務2同時執行,全部任務都執行成功後回到主線程
(效率高)
NSLog(@"當前線程 %@", [NSThread currentThread]); //(1)建立一個隊列組 dispatch_group_t group= dispatch_group_create(); //(2)開啓任務1 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"任務1 %@", [NSThread currentThread]); } }); //(3)開啓任務2 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"任務2 %@", [NSThread currentThread]); } }); //(4)全部任務執行完畢,回到主線程 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"主線程(刷新UI) %@", [NSThread currentThread]); }); //輸出: 當前線程 <NSThread: 0x7fd951704e70>{number = 1, name = main} //輸出: 任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)} //輸出: 任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)} //輸出: 任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)} //輸出: 任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)} //輸出: 任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)} //輸出: 任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)} //輸出: 任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)} //輸出: 主線程(刷新UI) <NSThread: 0x7ff65a406fb0>{number = 1, name = main}
一個任務執行完畢後,再執行下一個任務
主隊列是GCD自帶的一種特殊的串行隊列,放在主隊列中的任務,都會放到主線程中執行
//(1)使用dispatch_queue_create函數建立串行隊列 //參數1: 隊列名稱 //參數2: 隊列屬性 (通常用NULL) dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL); //(2)使用主隊列(跟主線程相關聯的隊列) dispatch_queue_t serialMainQueue = dispatch_get_main_queue();
多個任務併發執行(自動開啓多個線程同時執行任務)
<u>併發功能只有在異步(dispatch_async)函數下才有效!!!</u>
GCD默認已經提供了全局的併發隊列,供整個應用使用,不須要手動建立
併發隊列優先級 | 快捷值 | 優先級 |
---|---|---|
DISPATCH_QUEUE_PRIORITY_HIGH | 2 | 高 |
DISPATCH_QUEUE_PRIORITY_DEFAULT | 0 | 中(默認) |
DISPATCH_QUEUE_PRIORITY_LOW | (-2) | 低 |
DISPATCH_QUEUE_PRIORITY_BACKGROUND | INT16_MIN | 後臺 |
//(1)使用dispatch_get_global_queue函數得到全局的併發隊列 //參數1: 優先級 //參數2: 暫時無用參數 (傳0) dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
(開啓新線程,併發執行任務)
NSLog(@"當前線程 %@", [NSThread currentThread]); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任務1 %@", [NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任務2 %@", [NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任務3 %@", [NSThread currentThread]); }); //輸出: 當前線程 <NSThread: 0x7fbe83c067c0>{number = 1, name = main} //輸出: 任務1 <NSThread: 0x7fb84160ed90>{number = 3, name = (null)} //輸出: 任務3 <NSThread: 0x7fb841752940>{number = 4, name = (null)} //輸出: 任務2 <NSThread: 0x7fb8414167b0>{number = 5, name = (null)}
(開啓新線程,串行執行任務)
NSLog(@"當前線程 %@", [NSThread currentThread]); //建立串行隊列 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL); dispatch_async(serialQueue, ^{ NSLog(@"任務1 %@", [NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"任務2 %@", [NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"任務3 %@", [NSThread currentThread]); }); //輸出: 當前線程 <NSThread: 0x7fc192e05380>{number = 1, name = main} //輸出: 任務1 <NSThread: 0x7fc192c16670>{number = 3, name = (null)} //輸出: 任務2 <NSThread: 0x7fc192c16670>{number = 3, name = (null)} //輸出: 任務3 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
(不會開啓新線程,併發執行任務失效!)
NSLog(@"當前線程 %@", [NSThread currentThread]); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ NSLog(@"任務1 %@", [NSThread currentThread]); }); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ NSLog(@"任務2 %@", [NSThread currentThread]); }); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ NSLog(@"任務3 %@", [NSThread currentThread]); }); //輸出: 當前線程 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main} //輸出: 任務1 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main} //輸出: 任務2 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main} //輸出: 任務3 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
(不會開啓新線程,串行執行任務)
NSLog(@"當前線程 %@", [NSThread currentThread]); //建立串行隊列 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL); dispatch_sync(serialQueue, ^{ NSLog(@"任務1 %@", [NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ NSLog(@"任務2 %@", [NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ NSLog(@"任務3 %@", [NSThread currentThread]); }); //輸出: 當前線程 <NSThread: 0x7f8c0b407f00>{number = 1, name = main} //輸出: 任務1 <NSThread: 0x7f8c0b407f00>{number = 1, name = main} //輸出: 任務2 <NSThread: 0x7f8c0b407f00>{number = 1, name = main} //輸出: 任務3 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}