天天看點

iOS OS X 和 iOS 中的多線程技術-4.1 (GCD)

//聯系人:石虎 QQ:1224614774 昵稱:嗡嘛呢叭咪哄

一、GCD 分發隊列

GCD 分發隊列是執行任務的有力工具。使用分發隊列,你可以異步或者阻塞執行任意多個 block 的代碼。你可以使用分發隊列來執行幾乎任何線程任務。GCD 提供了簡單易用的接口。

二、在 GCD 中存在三種隊列:

1 串行分發隊列(Serial dispatch queue)

串行分發隊列又被稱為私有分發隊列,按順序執行隊列中的任務,且同一時間隻執行一個任務。串行分發隊列常用于實作同步鎖。下面代碼建立了一個串行分發隊列:

dispatch_queue_t serialQueue = dispatch_queue_create("com.example.MyQueue",NULL);

2 并發分發隊列(Concurrent dispatch queue)

串行分發隊列又被稱為全局分發隊列,也按順序執行隊列中的任務,但是順序開始的多個任務會并發同時執行。并發分發隊列常用于管理并發任務。下面代碼建立了一個并發分發隊列:

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

3 主分發隊列(Main dispatch queue)

主分發隊列是一個全局唯一的特殊的串行分發隊列。隊列中的任務會被在應用的主線程中執行。主分發隊列可以用于執行 UI 相關的操作。取得主分發隊列的方法:

dispatch_queue_t mainQueue = dispatch_get_main_queue();

GCD 任務執行方式

GCD 中有兩種任務執行方式:

異步執行, dispatch_async,意味将任務放入隊列之後,主線程不會等待 block 的傳回結果,而是立即繼續執行下去。

阻塞執行, dispatch_sync,意味将任務放入隊列之後,主線程被阻塞,需要等待 block 的執行結果傳回,才能繼續執行下去。

GCD 的其他主題

GCD 有着豐富的功能,比如分發組(dispatch group),信号(semaphores),分發栅欄(dispatch barrier),分發源(dispatch source)等等。這些可以用于完成更複雜的多線程任務。詳細可以查閱 Apple 關于 GCD 的文檔。

使用建議

在能夠使用 GCD 的地方,盡量使用 GCD

Apple 公司宣稱其在 GCD 技術中為更好地利用多核硬體系統做了很多的優化。是以,在性能方面 GCD 是不用擔心的。而且 GCD 也提供了相當豐富的 API,幾乎可以完成絕大部分線程相關的程式設計任務。是以,在多線程相關主題的程式設計中,GCD 應該是首選。下面舉一些可以推薦使用 GCD 的實際例子:

1 使用 GCD 的 dispatch queue 實作同步鎖

同步鎖的實作方案有不少,比如,如果僅僅是想對某個執行個體變量的讀寫操作加鎖,可以使用屬性(property)的 atomic 參數,對于一段代碼加鎖可以使用 @synchronized 塊,或者 NSLock。

@synchronized 和 NSLock 實作的同步鎖:

// Method 1

- (void)synchronizedMethod {

    @synchronized(self) {

        // safe

    }

}

// Method 2

_lock = [[NSLock alloc] init];

- (void)synchronizedMethod {

    [_lock lock];

    // Safe

    [_lock unlock];

}

@synchronized 一般會以 self 為同步對象。重複調用 @synchronized(self) 是很危險的。如果多個屬性這麼做,每一個屬性将會被和其它所有屬性同步,這可能并不是你所希望的,更好的方法是每個屬性的鎖都是互相獨立的。

另一種方法是使用 NSLock 實作同步鎖,這個方法不錯,但是缺點是在極端環境下同步塊可能會導緻鎖死,而且這種情況下處理鎖死狀态會有麻煩。

一個替代方法是使用 GCD 的分發隊列。将讀和寫分發到相同并發隊列中,這樣讀操作會是并發的,多個線程可以同時執行寫操作;而對于寫操作,以分發栅欄(dispatch barrier)保證同時隻有一個線程可以執行寫操作,并且由于寫操作無需傳回,寫操作還是異步馬上傳回的。這樣,就得到了一個高效且線程安全的鎖。代碼看起來會像這樣:

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

- (NSInteger)cake {

    __block NSInteger localCake;

    dispatch_sync(_syncQueue, ^{

        localCake = _cake;

    });

    return localCake;

}

- (void)setCake:(NSInteger)cake {

    dispatch_barrier_async(_syncQueue, ^{

        _cake = cake;

    });

}

簡單而言,上面的代碼可以使讀操作被競争執行;寫操作被互斥執行,并且異步傳回。使用 GCD 實作的這個同步鎖應該是效率最優且最安全的。

謝謝!!!