iOS多線程GCD簡介(一)

以前講過多線程之NSOperation,今天來說講代碼更加簡潔和高效的GCD。下面說的內容都是基於iOS6之後和ARC下。編程

Grand Central Dispatch (GCD)簡介

Grand Central Dispatch(GCD) 是異步執行任務的技術之一。開發者只須要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。因爲線程管理是做爲系統的一部分來實現的,所以能夠統一管理,也可執行任務,這樣就比之前的線程更有效率。GCD用很是簡潔的代碼,就能夠實現多線程編程。多線程

這篇主要講Dispatch Queue的一些基本東西,後續會加入其餘相關內容的介紹。併發

Dispatch Queue 種類

Dispatch Queue是執行處理的等待隊列,經過調用dispatch_async等函數,以block的形式將任務追加到Dispatch Queue中。Dispatch Queue按照添加進來的順序(FIFO)執行任務處理。可是在任務執行處理方式上,分爲Serial Dispatch QueueConcurrent 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:線程

名稱 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的時候,必定要小心,否則就會造成死鎖。

相關文章
相關標籤/搜索