GCD即為Grand Central Dispatch的縮寫,是一種主要用於非同步處理任務的安全的高性能解決方案。 在此不對比其他非同步處理技術,只記錄GCD的使用及說明。 先記錄一些必要的概念:線程,同步,非同步,並行隊列,串列隊列 線程:程式中任務執行流的最小單元。一個應用程式,一般存在一個進程(擁有 ...
GCD即為Grand Central Dispatch的縮寫,是一種主要用於非同步處理任務的安全的高性能解決方案。
在此不對比其他非同步處理技術,只記錄GCD的使用及說明。
先記錄一些必要的概念:線程,同步,非同步,並行隊列,串列隊列
線程:程式中任務執行流的最小單元。一個應用程式,一般存在一個進程(擁有獨立記憶體空間),而每個進程可以有多個線程,即多個任務執行流,類似於工廠車間的流水線。每個應用程式至少存在一個線程,即為主線程,作為唯一可以控制UI的線程。
同步:主要指多個任務在同一個線程上依次執行。
非同步:主要指多個任務在不同線程上同時執行。
隊列:C語言中的隊列指先進先出(FIFO)的數據結構,與棧的先進後出(FILO)相反。在GCD中分DISPATCH_QUEUE_SERIAL(串列隊列)和DISPATCH_QUEUE_CONCURRENT(並行隊列)。
並行隊列:主要指隊列中多個任務可以同時執行。
串列隊列:主要指隊列中多個任務只能依次執行。
逐個記錄重要的方法:
dispatch_queue_t dispatch_get_main_queue(void)
獲取主線程關聯的隊列。主隊列是串列隊列,該隊列中任務都將由主線程執行,UI相關的操作必須加入此隊列。
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
獲取全局通用的並行隊列。第一個參數一般指定優先順序,有如下選項:
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
第二個參數,用於標記,一般傳入0即可。
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
自定義創建一個派發隊列。第一個參數作為描述,例如"newcustomqueue";第二個參數一般指定隊列內任務是串列(DISPATCH_QUEUE_SERIAL)還是並行(DISPATCH_QUEUE_CONCURRENT)類型。
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
將一個block加入隊列,非同步執行。
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
將一個block加入隊列,同步執行。
使用和測試以上方法
定義了幾個隊列:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t customSerialQueue = dispatch_queue_create("customserialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customconcurrentqueue", DISPATCH_QUEUE_CONCURRENT);
使用示例一:
總結:
上述代碼,在同一次運行中,執行了多次,其中兩次截圖如上。
1.主隊列必定在主線程中執行
2.在主線程中,使用dispatch_async非同步執行非主隊列中任務,將產生新的子線程
使用示例二:
總結:
主線程卡死。該方法在block執行完之前不會return,而block也是在當前線程執行,所以互相等待,造成死鎖。
在主線程中使用同步執行主隊列任務,將死鎖。
使用示例三:
總結:
子線程卡死,UI依然可以操作。
綜合示例二和三,得出:在執行(同步或者非同步)串列隊列的線程中再使用dispatch_sync同步執行當前串列隊列,將造成線程死鎖。
使用示例四:
總結:
調用dispatch_sync將在當前線程上依次執行添加的任務,不管是否在同一隊列中,也不管隊列類型。
使用示例五:
LOG(@"測試GCD dispatch_async混合dispatch_sync"); LOG(@"mainThread : %@", [NSThread mainThread]); dispatch_async(customSerialQueue, ^{ LOG(@"level 1 dispatch_async No.1 customSerialQueue : %@", [NSThread currentThread]); dispatch_async(customSerialQueue, ^{ LOG(@"level 2 dispatch_async No.1 customSerialQueue : %@", [NSThread currentThread]); }); dispatch_async(customSerialQueue, ^{ LOG(@"level 2 dispatch_async No.2 customSerialQueue : %@", [NSThread currentThread]); }); dispatch_async(customSerialQueue, ^{ LOG(@"level 2 dispatch_async No.3 customSerialQueue : %@", [NSThread currentThread]); }); //============================== dispatch_async(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_async No.4 customConcurrentQueue : %@", [NSThread currentThread]); }); dispatch_async(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_async No.5 customConcurrentQueue : %@", [NSThread currentThread]); }); dispatch_async(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_async No.6 customConcurrentQueue : %@", [NSThread currentThread]); }); //============================== dispatch_sync(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_sync No.7 customConcurrentQueue : %@", [NSThread currentThread]); }); dispatch_sync(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_sync No.8 customConcurrentQueue : %@", [NSThread currentThread]); }); dispatch_sync(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_sync No.9 customConcurrentQueue : %@", [NSThread currentThread]); }); }); dispatch_async(customConcurrentQueue, ^{ LOG(@"level 1 dispatch_async No.2 customConcurrentQueue : %@", [NSThread currentThread]); dispatch_sync(customSerialQueue, ^{ LOG(@"level 2 dispatch_sync No.10 customSerialQueue : %@", [NSThread currentThread]); }); dispatch_sync(customSerialQueue, ^{ LOG(@"level 2 dispatch_sync No.11 customSerialQueue : %@", [NSThread currentThread]); }); dispatch_sync(customSerialQueue, ^{ LOG(@"level 2 dispatch_sync No.12 customSerialQueue : %@", [NSThread currentThread]); }); //============================== dispatch_async(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_async No.13 customConcurrentQueue : %@", [NSThread currentThread]); }); dispatch_async(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_async No.14 customConcurrentQueue : %@", [NSThread currentThread]); }); dispatch_async(customConcurrentQueue, ^{ LOG(@"level 2 dispatch_async No.15 customConcurrentQueue : %@", [NSThread currentThread]); }); //============================== dispatch_async(customSerialQueue, ^{ LOG(@"level 2 dispatch_async No.16 customSerialQueue : %@", [NSThread currentThread]); }); dispatch_async(customSerialQueue, ^{ LOG(@"level 2 dispatch_async No.17 customSerialQueue : %@", [NSThread currentThread]); }); dispatch_async(customSerialQueue, ^{ LOG(@"level 2 dispatch_async No.18 customSerialQueue : %@", [NSThread currentThread]); }); });
總結:
1.串列隊列customSerialQueue,只關註任務加入的先後順序,不管是同步還是非同步執行,總體順序不變
2.多個同步執行的同一串列隊列,如果加入了另一個執行隊列,他們的執行線程相同
3.多個非同步執行的同一串列隊列,如果加入了另一個執行隊列,他們的執行線程相同
4.綜合上述,使用dispatch_sync,一定是在當前線程執行;使用dispatch_async,不一定產生新的子線程,如果在同一級下,執行串列隊列,將使用已存在的同一線程。
5.多個嵌套的非同步執行並行隊列,可能使用已存在的閑置線程
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
該方法與dispatch_async類似,將block加入隊列非同步執行。註意當輪到該block執行時候,唯一非同步執行,執行完畢後才會執行其他任務。所以只有加入並行隊列,該方法才有意義。特別適用於加鎖操作。
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
將block將入隊列,延遲一定時間後非同步執行。
使用示例:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//code to be executed after a specified delay
});
表示在5秒以後,將block加入主隊列非同步執行。
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
將block加入隊列指定次數。隊列可以是並行的,所以block執行可以為並行。size_t表示加入時的索引。
測試代碼:
dispatch_apply(10, globalQueue, ^(size_t index) {
LOG(@"dispatch_apply : %zu", index);
});
2016-09-30 16:57:33.023 base[33146:8699303] dispatch_apply : 0 2016-09-30 16:57:33.023 base[33146:8699545] dispatch_apply : 1 2016-09-30 16:57:33.023 base[33146:8699530] dispatch_apply : 2 2016-09-30 16:57:33.024 base[33146:8699631] dispatch_apply : 3 2016-09-30 16:57:33.024 base[33146:8699303] dispatch_apply : 4 2016-09-30 16:57:33.024 base[33146:8699303] dispatch_apply : 8 2016-09-30 16:57:33.024 base[33146:8699303] dispatch_apply : 9 2016-09-30 16:57:33.024 base[33146:8699545] dispatch_apply : 5 2016-09-30 16:57:33.024 base[33146:8699530] dispatch_apply : 6 2016-09-30 16:57:33.024 base[33146:8699631] dispatch_apply : 7
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
只執行block中代碼一次。特別適用於單例模式。
使用示例:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
該方法將加入隊列的block分組管理,特別適用於多方法並行,最後整合全部結果。第一個參數由如下方法得到:
dispatch_group_t dispatch_group_create(void);
void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
該方法用於group中所有任務都完成後,回調block內容。
示例代碼:
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, globalQueue, ^{ LOG(@"dispatch_group_async : 1"); }); dispatch_group_async(group, globalQueue, ^{ LOG(@"dispatch_group_async : 2"); }); dispatch_group_async(group, globalQueue, ^{ LOG(@"dispatch_group_async : 3"); }); dispatch_group_async(group, globalQueue, ^{ LOG(@"dispatch_group_async : 4"); }); dispatch_group_notify(group, globalQueue, ^{ LOG(@"dispatch_group_async : completion"); }); 2016-09-30 17:19:15.490 base[33322:8718096] dispatch_group_async : 1 2016-09-30 17:19:15.490 base[33322:8718097] dispatch_group_async : 3 2016-09-30 17:19:15.490 base[33322:8718042] dispatch_group_async : 2 2016-09-30 17:19:15.490 base[33322:8718098] dispatch_group_async : 4 2016-09-30 17:19:15.491 base[33322:8718098] dispatch_group_async : completion
以上記錄了GCD的常見使用方法和示例代碼。