以前講過多線程之NSOperation,今天來說講代碼更加簡潔和高效的GCD。下面說的內容都是基於iOS6之後和ARC下。編程
Grand Central Dispatch(GCD) 是異步執行任務的技術之一。開發者只須要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。因爲線程管理是做爲系統的一部分來實現的,所以能夠統一管理,也可執行任務,這樣就比之前的線程更有效率。GCD用很是簡潔的代碼,就能夠實現多線程編程。多線程
這篇主要講Dispatch Queue的一些基本東西,後續會加入其餘相關內容的介紹。併發
Dispatch Queue是執行處理的等待隊列,經過調用dispatch_async
等函數,以block
的形式將任務追加到Dispatch Queue中。Dispatch Queue按照添加進來的順序(FIFO)執行任務處理。可是在任務執行處理方式上,分爲Serial Dispatch Queue
和Concurrent Dispatch Queue
。二者的區別如表格所示異步
Dispatch Queue分類 | 說明 |
---|---|
Serial Dispatch Queue | 串行的隊列,每次只能執行一個任務,而且必須等待前一個執行任務完成 |
Concurrent Dispatch Queue | 一次能夠併發執行多個任務,沒必要等待執行中的任務完成 |
下面用代碼來演示下:async
dispatch_async(queue, ^{ NSLog(@"1"); }); dispatch_async(queue, ^{ NSLog(@"2"); }); dispatch_async(queue, ^{ NSLog(@"3"); }); dispatch_async(queue, ^{ NSLog(@"4"); });
若是上面的queue是Serial Dispatch Queue
的話,那麼輸出的結果必定是1,2,3,4。由於執行順序是肯定的,而且後續的任務必須在以前的任務執行完成後才能執行。函數
若是是Concurrent Dispatch Queue
的話,那麼輸出的結果就不必定是1,2,3,4了。由於這些任務都是併發執行,而且不須要等待執行中的任務完成,若是其中任意一個任務完成將當即執行後面的任務。spa
在自定義建立前,咱們先看看系統爲咱們提供的幾個全局的Dispatch Queue
:線程
名稱 | Dispatch Queue 的種類 | 說明 |
---|---|---|
Main Dispatch Queue | Serial Dispatch Queue | 主線程執行 |
Global Dispatch Queue (HIGH) | Concurrent Dispatch Queue | 執行優先級:高 |
Global Dispatch Queue (DEFAULT) | Concurrent Dispatch Queue | 執行優先級:默認 |
Global Dispatch Queue (LOW) | Concurrent Dispatch Queue | 執行優先級:低 |
Global Dispatch Queue (BACKGROUND) | Concurrent Dispatch Queue | 執行優先級:後臺 |
從表格中咱們能夠知道咱們的主線程就是Serial Dispatch Queue
,而以後的三種Dispatch Queue 則是Concurrent Dispatch Queue
。這也是爲何咱們不能把耗時的任務放在主線程裏面去操做。3d
若是沒有特殊需求,咱們能夠直接獲取這些queue來執行咱們的任務:調試
//主線程 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //HIGH dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); //DEFAULT dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //LOW dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //BACKGROUND dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
下面看看如何自定義一個queue:
//串行隊列 dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.serialQueue", DISPATCH_QUEUE_SERIAL); //併發隊列 dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
經過調用dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
這個函數。第一個參數是給這個queue起的標識,這個在調試的能夠看到是哪一個隊列在執行,或者在crash日誌中,也能作爲提示。第二個是須要建立的隊列類型,是串行的仍是併發的。固然你也能夠經過dispatch_queue_get_label(dispatch_queue_t queue)
獲取你建立queue的名字。
這個也是咱們使用最多的地方,咱們直接調用dispatch_async
這個函數,就能夠將咱們要追加的任務添加到隊列裏面,並當即返回,異步的執行。這個很少講。
dispatch_async(queue, ^{ NSLog(@"1"); });
這點咱們可能用得不是不少,可是一用很差就出現問題了。當調用這個dispatch_sync
函數的時候,這個線程將不會當即返回,直到這個線程執行完畢。看下下面的代碼:
dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"2"); });
若是你在主線程裏面調用這個函數,那麼,很遺憾,主線程將被卡主3秒鐘。當主線程調用這個方法的時候,因爲是同步,不會當即返回,直到這個裏面內容執行完畢才能返回。這個時候無論你這個queue是什麼類型,都同樣。既然不能當即返回,咱們能夠在一個異步執行的線程中,再去調用這個同步方法。
dispatch_async(serialQueue, ^{ NSLog(@"4"); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"5"); }); NSLog(@"6"); });
那這個時候,調用這個方法的時候這個線程將當即返回。而同步在這個線程裏面執行。這個時候將輸出,4,5,6的結果。
同步執行的死鎖問題
如今把上面代碼拿出來改下
dispatch_async(serialQueue, ^{ NSLog(@"4"); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"5"); }); NSLog(@"6"); });
這個時候,我把queue的類型設置爲串行的類型。這個時候將只會輸出4。爲何呢?系統調用這個線程的時候,首先輸出4,而後繼續執行這個裏面的同步線程。因爲個人這個queue是串行的,也就是後續的任務必須在以前的執行中任務完成後才能繼續執行。可是這個同步執行的線程不會當即返回,必須等到它執行完成才能返回。這樣最外面的任務無法執行完,而裏面的同步線程又不能當即返回,因此就造成了死鎖。
所以在你的編程中使用這個API的時候,必定要小心,否則就會造成死鎖。