GCD之隊列的實現和使用

1、什麼是GCD?編程

如下是摘自蘋果的官方說明。安全

Grand Central Dispatch(GCD)是異步執行任務的技術之一。通常將應用程序中記述的線程管理用的代碼在系統級中實現。開發者只須要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。多線程

 

2、GCD實現之Dispatch Queue併發

  • 用於管理追加的Block的C語言層實現的FIFO隊列
  • Atomic函數中實現的用於排他控制的輕量級信號
  • 用於管理線程的C語言層實現的一些容器

GCD的API所有包含在libdispatch庫中的C語言函數。Dispatch Queue經過結構體和鏈表,被實現爲FIFO隊列。FIFO隊列管理是經過dispatch_async等函數所追加的Block。異步

Block並非直接加入FIFO隊列,而是先加入Dispatch Continuation這一dispatch_continution_t類結構體中,而後再加入FIFO隊列。該Dispatch Continuation用於記憶Block所屬的Dispatch Group和其餘一些信息,至關於通常常說的執行上下文。async

Dispatch Queue可經過dispatch_set_target_queue函數設定,能夠設定執行該Dispatch Queue處理的Dispatch Queue爲目標。該目標可像串珠子同樣,設定多個鏈接在一塊兒的Dispatch Queue。可是在鏈接串的最後必須設定爲Main Dispatch Queue,或各類優先級的Global Dispatch Queue,或是準備用於Serial Dispatch Queue的各類優先級的 Global Dispatch Queue.函數

Global Dispatch Queue有以下8種。spa

  • Global Dispatch Queue (High Priority)
  • Global Dispatch Queue (Default Priority)
  • Global Dispatch Queue (Low Priority)
  • Global Dispatch Queue (Background Priority)
  • Global Dispatch Queue (High Overcommit Priority)
  • Global Dispatch Queue (Default Overcommit Priority)
  • Global Dispatch Queue (Low Overcommit Priority)
  • Global Dispatch Queue (Background Overcommit Priority)

優先級中附有Overcommit的Global Dispatch Queue使用在Serial Dispatch Queue中。如Overcommit這個名稱所示,無論系統狀態如何,都會強制生成線程的Dispatch Queue。線程

這8種Global Dispatch Queue各使用1個pthread_workqueue。GCD初始化時,使用pthread_workqueue_create_np函數生成pthread_workqueue。 調試

pthread_workqueue包含在Libc提供的pthreads API中。其使用bsdthread_register和workq_open系統調用,在初始化XNU內核的workqueue以後獲取workqueue信息。

XNU內核持有4中workqueue。

  • WORKQUEUE_HIGH_PRIOQUEUE
  • WORKQUEUE_DEFAULT_PRIOQUEUE
  • WORKQUEUE_LOW_PRIOQUEUE
  • WORKQUEUE_BG_PRIOQUEUE

以上爲4種執行優先級的workqueue。該執行優先級與Global Dispatch Queue的4種執行優先級相同。

 

Dispatch Queue中執行Block的過程。

當在Global Dispatch Queue中執行Block時,libdispatch從Global Dispatch Queue自身的FIFO隊列中取出Dispatch Continuation,調用pthread_workqueue_additem_np函數。將該Global Dispatch Queue自身、符合其優先級的workqueue信息以及爲執行Dispatch Continuation的回調函數等傳遞給參數。

  

pthread_workqueue_additem_np函數使用workq_kernreturn系統調用,通知workqueue增長應當執行的項目。根據該通知,XNU內核基於系統狀態判斷是否須要生成線程。若是是Overcommit優先級的Global Dispatch Queue,workqueue則始終生成線程。

workqueue的線程執行pthread_workqueue函數,該函數調用libdispatch的回調函數。在該回調函數中執行加入到Dispatch Continuation的Block。

Block執行結束後,進行通知Dispatch Group結束、釋放Dispatch Continuation等處理,開始準備執行加入到Global Dispatch Queue中的下一個Block。

以上就是Dispatch Queue執行的大概過程。

 

3、線程和隊列

線程是代碼執行的路徑,隊列則是用於保存以及管理任務的,線程負責去隊列中取任務進行執行。

1、隊列

是管理線程的,至關於線程池,能管理線程何時執行。

隊列分爲串行隊列和並行隊列等

串行隊列:隊列中的任務按順序執行

並行隊列:隊列中的任務會併發執行。任務執行完畢了,不必定出隊列。只有前面的任務執行完了,纔會出隊列。

 

串行隊列:隊列中的任務只會順序執行,多個串行隊列可並行執行

dispatch_queue_t q = dispatch_queue_create(「xxx」,DISPATCH_QUEUE_SERIAL);

 

並行隊列:隊列中的任務會併發執行

dispatch_queue_t q = dispatch_queue_create(「xxx」, DISPATCH_QUEUE_CONCURRENT);

 

全局隊列:與並行隊列相似,但調試時,沒法確認操做所在隊列

dispatch_queue_t q = dispatch_get_global_queue(dispatch_queue_priority_default, 0);

 

主隊列:每個程序對應惟一一個主隊列;在多線程開發中,使用主隊列更新UI

dispatch_queue_t q = dispatch_get_main_queue();

 

二、同步和異步

dispatch_async (異步操做函數),就是將指定的Block「非同步」地追加到指定的隊列(queue)中。dispatch_async函數不作任何等待。會新開線程

 

 

 

 

dispatch_sync( 同步操做函數),就是將指定的Block「同步」地追加到指定的隊列(queue)中。在追加Block結束以前,dispatch_sync函數會一直等待;不會新開線程

 

 

   

三、隊列和操做的組合

串行隊列同步操做:同步操做不會新開線程、操做順序執行

串行隊列異步操做:異步操做新開一個子線程、操做順序執行,「最安全的選擇」

並行隊列同步操做:同步操做不會新開線程、操做順序執行

並行隊列異步操做:異步操做會新開多個線程(有多少任務,就開n個線程執行)、操做無序執行;隊列前若是有其餘任務,會等待前面的任務完成以後再執行;場景:既不影響主線程,又不須要順序執行的操做! 

全局隊列異步操做:異步操做會新建多個線程、操做無序執行,隊列前若是有其餘任務,會等待前面的任務完成以後再執行

全局隊列同步操做:同步操做不會新建線程、操做順序執行

主隊列異步操做:異步操做都在主線程上順序執行的,不存在異步的概念

主隊列同步操做:會死鎖

 

四、會引發死鎖的2種狀況

一、在主線程中運用主隊列同步。

- (void)viewDidLoad {

    [super viewDidLoad];

    dispatch_sync(dispatch_get_main_queue(), ^{      

        NSLog(@"hello");

    });

}

同步對於任務是馬上執行的,那麼當把任務放進主隊列時,它就會立馬執行,只有執行完這個任務,viewDidLoad纔會繼續向下執行。

而viewDidLoad和任務都是在主隊列上的,因爲隊列的先進先出原則,任務又需等待viewDidLoad執行完畢後才能繼續執行,viewDidLoad和這個任務就造成了相互循環等待,就形成了死鎖。

想避免這種死鎖,能夠將同步改爲異步dispatch_async,或者將dispatch_get_main_queue換成其餘串行或並行隊列,均可以解決。

 

 二、在串行隊列中同步的向這個串行隊列追加Block

 dispatch_queue_t serialQueue = dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);

 dispatch_async(serialQueue, ^{

        dispatch_sync(serialQueue, ^{

            NSLog(@"hello");

        });

});

想避免這種死鎖,能夠將同步改爲異步dispatch_async,或者將串行隊列換爲並行隊列,均可以解決。

 

以上部份內容參考自《Objective-C高級編程》一書

相關文章
相關標籤/搜索