GCD(Grand Central Dispatch)
1. 什么是GCD
GCD實現(xiàn)了異步執(zhí)行任務(wù)。開發(fā)者只需將自己定義的任務(wù)追加到合適的派發(fā)隊列(Dispatch Queue)中,GCD就能生成必要的線程執(zhí)行自定義的任務(wù)。
2. 派發(fā)隊列(Dispatch Queue)
派發(fā)隊列是執(zhí)行任務(wù)的等待隊列,按照追加任務(wù)的順序(先進先出)執(zhí)行任務(wù)。
GCD中的派發(fā)隊列分為兩種,一種是等待正在執(zhí)行的任務(wù)的串行派發(fā)隊列(Serial Dispatch Queue),另一種是不等待正在執(zhí)行中的任務(wù)的并行派發(fā)隊列(Concurrent Dispatch Queue)。
串行隊列(Serial Dispatch Queue)
- 隊列中的任務(wù)會等待正在執(zhí)行的任務(wù)執(zhí)行結(jié)束,有序執(zhí)行(排隊執(zhí)行)
- 一個串行隊列只生成并使用一個線程,只能執(zhí)行向它追加的任務(wù)
- 如果生成了多個串行隊列,這些串行隊列將并發(fā)執(zhí)行
- 在多個線程要更新相同的資源,導(dǎo)致的數(shù)據(jù)競爭時,使用串行隊列
- Dispatch Queue系統(tǒng)中為我們提供的串行隊列是Main Dispatch Queue,即在主線程中執(zhí)行的派發(fā)隊列。追加到Main Dispatch Queue的任務(wù)都是在主線程的RunLoop中執(zhí)行,例如用戶界面的更新操作。
//創(chuàng)建一個串行隊列
dispatch_queue_t myQueue = dispatch_queue_create("com.example.gcd", NULL);
// 獲得主線程隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
并發(fā)隊列(Concurrent Dispatch Queue)
- 不用等待正在執(zhí)行的任務(wù)處理結(jié)束,可以并發(fā)執(zhí)行多個任務(wù),此時并發(fā)執(zhí)行的任務(wù)數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài)。
- 并發(fā)隊列中的任務(wù)不能產(chǎn)生數(shù)據(jù)競爭問題
- Dispatch Queue系統(tǒng)中為我們提供的并發(fā)隊列Global Dispatch Queue ,是所有應(yīng)用程序都能使用的并發(fā)隊列,需要在程序中使用并發(fā)隊列只需將獲得Global Dispatch Queue。
- Global Dispatch Queue有四個優(yōu)先級,分別為高優(yōu)先級(High Priority)、默認優(yōu)先級(Default Priority)、低優(yōu)先級(Low Priority)和后臺優(yōu)先級(Background Priority)。需要注意的是,在向Global Dispatch Queue中追加任務(wù)時,應(yīng)選擇與任務(wù)的優(yōu)先級相同的Global Dispatch Queue。但是用于Global Dispatch Queue的線程并不保證實時性,其執(zhí)行的優(yōu)先級只是大致的判斷。
//創(chuàng)建一個并行隊列
dispatch_queue_t myQueue = dispatch_queue_create("com.example.gcd", DISPATCH_QUEUE_CONCURRENT);
// 獲得默認優(yōu)先級的全局隊列
dispatch_queue_t myQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3. GCD中常見API
為了防止我說的不準確,下面列舉了一些我認為重要的官方的API
- dispatch_set_target_Queue 變更生成的Dispatch Queue的優(yōu)先級
void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
- dispatch_after 并不在指定的時間后執(zhí)行,而是在指定時間后,追加任務(wù)到派發(fā)隊列中去。
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
- Dispatch Group
GCD中的特性,能夠?qū)㈥犃兄械娜蝿?wù)分組;
在等待的派發(fā)隊列中的任務(wù)執(zhí)行完畢后,追加新任務(wù)到派發(fā)隊列中。
此時可以使用: - dispatch_group_notify函數(shù)等待分組中的任務(wù)執(zhí)行完畢,再追加新任務(wù)到指定隊列中。
void dispatch_group_notify(
dispatch_group_t group, // The dispatch group to observe.
dispatch_queue_t queue, // The queue to which the supplied block will be submitted when the group completes.
dispatch_block_t block // The block to submit when the group completes.
);
- dispatch_group_wait函數(shù),等待分組中的任務(wù)執(zhí)行結(jié)束,當(dāng)其返回值為0時,表示分組中的任務(wù)執(zhí)行完畢。
/*
* Returns zero on success (all blocks associated with the group completed
* within the specified timeout) or non-zero on error (i.e. timed out).
*/
long dispatch_group_wait(
dispatch_group_t group,
dispatch_time_t timeout // When to timeout (see dispatch_time). As a convenience, there are the DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants.
);
- dispatch_barrier_async 柵欄
柵欄必須單獨執(zhí)行,不能與其他任務(wù)并發(fā)執(zhí)行,因此,柵欄只對并發(fā)隊列有意義。
柵欄只有等待當(dāng)前隊列所有并發(fā)任務(wù)都執(zhí)行完畢后,才會單獨執(zhí)行,待其執(zhí)行完畢,再按照正常的方式繼續(xù)向下執(zhí)行。
執(zhí)行過程
(1)等待追加到并發(fā)隊列上的任務(wù)全部執(zhí)行完
(2)再將dispatch_barrier_async中指定的處理任務(wù)追加到該并發(fā)隊列中
(3)等待dispatch_barrier_async中追加的任務(wù)處理完畢后
(4)并發(fā)隊列恢復(fù)正常操作
可以提高訪問數(shù)據(jù)庫和文件的效率和安全性
/*
* block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues).
*/
void dispatch_barrier_async(
dispatch_queue_t queue, // The target dispatch queue to which the block is submitted.The system will hold a reference on the target queue until the block has finished.
dispatch_block_t block // The block to submit to the target dispatch queue. This function performs Block_copy() and Block_release() on behalf of callers.
);
- dispatch_apply 按照指定的次數(shù)將指定的塊追加到指定的派發(fā)隊列中,并等待全部任務(wù)執(zhí)行結(jié)束
/*
* Each invocation of the block will be passed the current index of iteration.
*/
void dispatch_apply(
size_t iterations, // The number of iterations to perform.
dispatch_queue_t queue, // The target dispatch queue to which the block is submitted.s
void (^block)(size_t) // The block to be invoked the specified number of iterations. The result of passing NULL in this parameter is undefined.
);
dispatch_suspend/dispatch_resume 掛起/恢復(fù)
對已經(jīng)執(zhí)行的任務(wù)并沒有作用,線程掛起后,派發(fā)隊列中從第一個未開始執(zhí)行的任務(wù)開始暫停執(zhí)行,線程恢復(fù)后,再由停止的地方重新開始執(zhí)行。單例的實現(xiàn)
(1)同步塊 @synchronization
會出現(xiàn)的問題:所有同步塊會彼此搶奪同一把鎖
(2)dispatch_once 可以執(zhí)行只需要運行一次的安全代碼,是線程安全,高效的。
/*
* A predicate for use with dispatch_once(). It must be initialized to zero.
* Note: static and global variables default to zero.
*/
typedef long dispatch_once_t;
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
4. 同步任務(wù)與異步任務(wù)
- 主要影響:能不能開啟新的線程
- 同步:只是在當(dāng)前線程中執(zhí)行任務(wù),不具有開啟新線程的能力。
dispatch_sync 同步執(zhí)行,等待任務(wù)處理結(jié)束
- 異步:可以在新線程中執(zhí)行任務(wù),具有開啟新線程的能力。
dispatch_async 異步執(zhí)行,不等待任務(wù)處理結(jié)束
5. 并行隊列和串行隊列
- 主要影響:任務(wù)的執(zhí)行方式
- 并發(fā):允許多個任務(wù)并發(fā)(同時)執(zhí)行。
- 串行:一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)。
6. 對比
| 并行隊列 | 串行隊列 | |
|---|---|---|
| 同步任務(wù) | 不需要創(chuàng)建線程 | 不需要新建線程 |
| 異步任務(wù) | 有多少個任務(wù),就開N個線程執(zhí)行 | 需要一個子線程(無需關(guān)心線程的創(chuàng)建和回收) |
7. GCD與NSOperationQueue的對比
- NSOperationQueue的底層實現(xiàn)都是用GCD實現(xiàn)的,它是對GCD的進一步的封裝。接下來就對比一下GCD與NSOperationQueue的功能:
- GCD的實現(xiàn)是由底層的C語言編寫,在隊列中執(zhí)行的任務(wù)都是塊代碼;而NSOperationQueue及相關(guān)類提供的對象都是OC的對象,NSOperation的對象提供了更多的操作。
- NSOperationQueue中可以隨時取消已經(jīng)設(shè)定即將要執(zhí)行,但尚未執(zhí)行的任務(wù)(已經(jīng)開始執(zhí)行的任務(wù)是無法停止執(zhí)行的)。而在GCD中并不是不能實現(xiàn)同樣的功能,而是并不像NSOperationQueue的操作那么簡單而已。
- NSOperation可以設(shè)置任務(wù)間的依賴關(guān)系,舉個簡單的例子:任務(wù)QA依賴任務(wù)QB,即使這兩個任務(wù)在同一個隊列中,前者等待后者執(zhí)行完后再執(zhí)行。
- NSOperation可以應(yīng)用KVO,監(jiān)聽任務(wù)是否完成/取消的狀態(tài)。
- 可以在NSOperation的對象中,設(shè)置任務(wù)的優(yōu)先級,可以使同一個并發(fā)隊列中的任務(wù)區(qū)分先后順序地執(zhí)行。而GCD中可以區(qū)分隊列間的先后順序,要區(qū)分隊列中任務(wù)的優(yōu)先級需要大量的代碼才能實現(xiàn)。
- NSOperation具有繼承性