讀書筆記【1】 Grand Central Dispatch

SuperDanny發表於2017-12-13

簡答的複習了一下GCD的一些簡單操作,這裡做一個筆記,方便以後檢視。

  • Dispatch Queue
    • 自定義串、並行佇列
    • 程式程式預設佇列
    • 主執行緒佇列
  • 同步/非同步、序列/並行使用
  • 死鎖問題
  • dispatch_after
  • dispatch_group

Dispatch Queue

  • Serial Dispatch Queue 按新增進佇列的順序(先進先出)一個接一個的執行

  • Concurrent Dispatch Queue 併發執行佇列裡的任務

1.1 自定義串、並行佇列

並行佇列:DISPATCH_QUEUE_CONCURRENT
序列佇列:DISPATCH_QUEUE_SERIAL
複製程式碼
//建立一個名稱為‘com.company.xxx’的序列佇列
dispatch_queue_t serialQueue = dispatch_queue_create("com.company.xxx", DISPATCH_QUEUE_SERIAL);

//建立一個名稱為‘com.xxx’的並行佇列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.company.xxx", DISPATCH_QUEUE_CONCURRENT);
複製程式碼

Note: 1)引數一是佇列的名稱,一般是使用倒序的全域名。雖然可以不給佇列指定一個名稱,但是有名稱的佇列可以讓我們在遇到問題時更好除錯 2)當引數二為nil時返回Serial Dispatch Queue(序列佇列),如上面那個例子,當指定為DISPATCH_QUEUE_CONCURRENT時返回Concurrent Dispatch Queue(並行佇列)

1.2 程式程式預設佇列

高優先順序佇列:DISPATCH_QUEUE_PRIORITY_HIGH
中優先順序佇列:DISPATCH_QUEUE_PRIORITY_DEFAULT
低優先順序佇列:DISPATCH_QUEUE_PRIORITY_LOW
複製程式碼
//獲取程式預設並行佇列,第二個引數固定為0
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
複製程式碼

Note: 1)需要注意的是,三個佇列不代表三個執行緒,可能會有更多的執行緒。併發佇列可以根據實際情況來自動產生合理的執行緒數,也可理解為dispatch佇列實現了一個執行緒池的管理,對於程式邏輯是透明的。 2)獲取Global Dispatch Queue的時候可以指定優先順序,可以根據自己的實際情況來決定使用哪種優先順序

1.3 主執行緒佇列

//獲取主執行緒序列佇列
dispatch_queue_t queue = dispatch_get_main_queue();
複製程式碼

三者對比

佇列 序列 並行
自定義佇列
程式預設佇列 ×
主執行緒佇列 ×

Note: 一般只在需要更新UI時我們才獲取Main Dispatch Queue,其他情況下用Global Dispatch Queue就滿足需求了

同步/非同步、序列/並行使用

//非同步執行block,函式立即返回
dispatch_async(queue, ^{

  //block具體程式碼

}); 

//同步執行block,函式不返回
dispatch_sync(queue, ^{

  //block具體程式碼

}); 
複製程式碼

舉例:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //子執行緒中開始網路請求資料
        ···
        //更新資料模型
        dispatch_sync(dispatch_get_main_queue(), ^{
            //在主執行緒中更新UI程式碼
            self.view.backgroundColor = [UIColor orangeColor];
        });
    });
複製程式碼

Note: 儘可能避免使用dispatch_sync,巢狀使用時還容易引起程式死鎖

死鎖問題

  • 同步序列會出現死鎖,同步並行不會造成死鎖
  • 非同步不管是序列還是並行都不會出現死鎖
  • 非同步巢狀同步或者同步巢狀非同步就需要注意程式碼執行順序

Note: 死鎖原因:提交到主執行緒佇列的時候,慎用同步dispatch_sync方法,有可能造成死鎖。因為主執行緒佇列是序列佇列,要等佇列裡的任務一個一個執行。所以提交一個任務到佇列,如果用同步方法就會阻塞住主執行緒,而主執行緒又要等主執行緒佇列裡的任務都執行完才能執行那個剛提交的,所以主執行緒佇列裡還有其他的任務的話,但他已經被阻塞住了,沒法先完成佇列裡的其他任務,即,最後一個任務也沒機會執行到,於是造成死鎖。

dispatch_after

dispatch_after能讓我們新增進佇列的任務延時執行,比如想讓一個Block在5秒後執行:

double delayTime = 5.0;

dispCatch_time_t dTime = dispatch_time(DISPATCH_TIME_NOW, 
(int64_t)(delayTime*NSEC_PER_SEC));

dispatch_after(dTime, dispatch_get_main_queue(), ^{
        //delayTime秒後執行block塊
        NSLog(@"block執行");
    });
複製程式碼

dispatch_after的真正含義是在5秒後把任務新增進佇列中,並不是表示在5秒後執行,大部分情況該函式能達到我們的預期,只有在對時間要求非常精準的情況下才可能會出現問題


【擴充】 延遲執行還有另外一種方式,那就是NSObject中的performSelector:withObject:afterDelay:以及performSelector:withObject:afterDelay:inModes:


dispatch_group

我們現在有3個Block要執行,我們不在乎它們執行的順序,我們只希望在這3個Block執行完之後再執行某個操作。這個時候就需要使用dispatch_group

dispatch_group_t groupQueue = dispatch_group_create();

dispatch_group_async(groupQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"1");
});

dispatch_group_async(groupQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"2");
});

dispatch_group_async(groupQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"3");
});

dispatch_group_notify(groupQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"completion");
});
複製程式碼

在控制檯列印的結果是: 2015-07-21 22:17:57.159 GCD[9606:1327380] 3 2015-07-21 22:17:57.159 GCD[9606:1327377] 2 2015-07-21 22:17:57.159 GCD[9606:1327379] 1 2015-07-21 22:17:57.159 GCD[9606:1327377] completion

Note: 輸出的順序與新增進佇列的順序無關,因為佇列是Concurrent Dispatch Queue,但“completion”的輸出一定是在最後的


再一次感謝您花費時間閱讀這篇文章!

微博: @Danny_呂昌輝
部落格: SuperDanny

2015 年 07月 15日

相關文章