GCD介紹(四): 完結

Dispatch Queue掛起 安全

dispatch queue能夠被掛起和恢復。使用 dispatch_suspend函數來掛起,使用  dispatch_resume 函數來恢復。這兩個函數的行爲是如你所願的。另外,這兩個函數也能夠用於dispatch source。 多線程

一個要注意的地方是,dispatch queue的掛起是block粒度的。換句話說,掛起一個queue並不會將當前正在執行的block掛起。它會容許當前執行的block執行完畢,而後後續的block再也不會被執行,直至queue被恢復。 函數

還有一個注意點:從man頁上得來的:若是你掛起了一個queue或者source,那麼銷燬它以前,必須先對其進行恢復。 測試

Dispatch Queue目標指定 spa

全部的用戶隊列都有一個目標隊列概念。從本質上講,一個用戶隊列其實是不執行任何任務的,可是它會將任務傳遞給它的目標隊列來執行。一般,目標隊列是默認優先級的全局隊列。 .net

用戶隊列的目標隊列能夠用函數 dispatch_set_target_queue來修改。咱們能夠將任意dispatch queue傳遞給這個函數,甚至能夠是另外一個用戶隊列,只要別構成循環就行。這個函數能夠用來設定用戶隊列的優先級。好比咱們能夠將用戶隊列的目標隊列設定爲低優先級的全局隊列,那麼咱們的用戶隊列中的任務都會以低優先級執行。高優先級也是同樣道理。 線程

有一個用途,是將用戶隊列的目標定爲main queue。這會致使全部提交到該用戶隊列的block在主線程中執行。這樣作來替代直接在主線程中執行代碼的好處在於,咱們的用戶隊列能夠單獨地被掛起和恢復,還能夠被重定目標至一個全局隊列,而後全部的block會變成在全局隊列上執行(只要你確保你的代碼離開主線程不會有問題)。 指針

還有一個用途,是將一個用戶隊列的目標隊列指定爲另外一個用戶隊列。這樣作能夠強制多個隊列相互協調地串行執行,這樣足以構建一組隊列,經過掛起和暫停那個目標隊列,咱們能夠掛起和暫停整個組。想象這樣一個程序:它掃描一組目錄而且加載目錄中的內容。爲了不磁盤競爭,咱們要肯定在同一個物理磁盤上同時只有一個文件加載任務在執行。而但願能夠同時從不一樣的物理磁盤上讀取多個文件。要實現這個,咱們要作的就是建立一個dispatch queue結構,該結構爲磁盤結構的鏡像。 code

首先,咱們會掃描系統並找到各個磁盤,爲每一個磁盤建立一個用戶隊列。而後掃描文件系統,併爲每一個文件系統建立一個用戶隊列,將這些用戶隊列的目標隊列指向合適的磁盤用戶隊列。最後,每一個目錄掃描器有本身的隊列,其目標隊列指向目錄所在的文件系統的隊列。目錄掃描器枚舉本身的目錄併爲每一個文件向本身的隊列提交一個block。因爲整個系統的創建方式,就使得每一個物理磁盤被串行訪問,而多個物理磁盤被並行訪問。除了隊列初始化過程,咱們根本不須要手動干預什麼東西。 隊列

信號量

dispatch的信號量是像其餘的信號量同樣的,若是你熟悉其餘多線程系統中的信號量,那麼這一節的東西再好理解不過了。

信號量是一個整形值而且具備一個初始計數值,而且支持兩個操做:信號通知和等待。當一個信號量被信號通知,其計數會被增長。當一個線程在一個信號量上等待時,線程會被阻塞(若是有必要的話),直至計數器大於零,而後線程會減小這個計數。

咱們使用函數  dispatch_semaphore_create 來建立dispatch信號量,使用函數  dispatch_semaphore_signal 來信號通知,使用函數 dispatch_semaphore_wait 來等待。這些函數的man頁有兩個很好的例子,展現了怎樣使用信號量來同步任務和有限資源訪問控制。

單次初始化

GCD還提供單詞初始化支持,這個與pthread中的函數  pthread_once 很類似。GCD提供的方式的優勢在於它使用block而非函數指針,這就容許更天然的代碼方式:

這個特性的主要用途是惰性單例初始化或者其餘的線程安全數據共享。典型的單例初始化技術看起來像這樣(線程安全的):

+ (id)sharedWhatever
    {
        static Whatever *whatever = nil;
        @synchronized([Whatever class])
        {
            if(!whatever)
                whatever = [[Whatever alloc] init];
        }
        return whatever;
    }

這挺好的,可是代價比較昂貴;每次調用  +sharedWhatever 函數都會付出取鎖的代價,即便這個鎖只須要進行一次。確實有更風騷的方式來實現這個,使用相似雙向鎖或者是原子操做的東西,可是這樣挺難弄並且容易出錯。

使用GCD,咱們能夠這樣重寫上面的方法,使用函數 dispatch_once

+ (id)sharedWhatever
    {
        static dispatch_once_t pred;
        static Whatever *whatever = nil;
        dispatch_once(&pred, ^{
            whatever = [[Whatever alloc] init];
        });
        return whatever;
    }

這個稍微比 @synchronized 方法簡單些,而且GCD確保以更快的方式完成這些檢測,它保證block中的代碼在任何線程經過  dispatch_once 調用以前被執行,但它不會強制每次調用這個函數都讓代碼進行同步控制。實際上,若是你去看這個函數所在的頭文件,你會發現目前它的實現實際上是一個宏,進行了內聯的初始化測試,這意味着一般狀況下,你不用付出函數調用的負載代價,而且會有更少的同步控制負載。

結論

這一章,咱們介紹了dispatch queue的掛起、恢復和目標重定,以及這些功能的一些用途。另外,咱們還介紹瞭如何使用dispatch 信號量和單次初始化功能。到此,我已經完成了GCD如何運做以及如何使用的介紹。

相關文章
相關標籤/搜索