GCD的API
1. Dispatch Queue
Dispatch Queue分為兩種
① DISPATCH_QUEUE_SERIAL 等待現(xiàn)在執(zhí)行中處理結(jié)束
② DISPATCH_QUEUE_CONCURRENT 不等待現(xiàn)在執(zhí)行中處理結(jié)束
如何得到Dispatch Queue?
方法一:通過GCD的API生成
- 生成 DISPATCH_QUEUE_SERIAL
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("gcd.MySerialDispatchQueue",
DISPATCH_QUEUE_SERIAL);
- 生成 DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("gcd.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
方法二:獲取系統(tǒng)提供的標(biāo)準(zhǔn)Dispatch Queue
| 名稱 | Dispatch Queue種類 | 說明 |
|---|---|---|
| Main Dispatch Queue | SerialDispatchQueue | 主線程執(zhí)行 |
| Global Dispatch Queue(High priority) | ConcurrentDispatchQueue | 執(zhí)行優(yōu)先級(jí):高 |
| Global Dispatch Queue(Default) | ConcurrentDispatchQueue | 執(zhí)行優(yōu)先級(jí):默認(rèn) |
| Global Dispatch Queue(Low) | ConcurrentDispatchQueue | 執(zhí)行優(yōu)先級(jí):低 |
| Global Dispatch Queue(Background) | ConcurrentDispatchQueue | 執(zhí)行優(yōu)先級(jí):后臺(tái) |
/*
* Main Dispatch Queue的獲取方法
*/
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
/*
* Global Dispatch Queue(高優(yōu)先級(jí))的獲取方法
*/
dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
/*
* Global Dispatch Queue(默認(rèn)優(yōu)先級(jí))的獲取方法
*/
dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
* Global Dispatch Queue(低優(yōu)先級(jí))的獲取方法
*/
dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
/*
* Global Dispatch Queue(后臺(tái)優(yōu)先級(jí))的獲取方法
*/
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

2. dispatch_set_target_queue(設(shè)定Dispatch Queue優(yōu)先級(jí))
作用: dispatch_queue_creat函數(shù)生成的Dispatch Queue 不管是 Serial Dispatch Queue還是Concurrent Dispatch Queue,都使用默認(rèn)優(yōu)先級(jí)Global Dispatch Queue相同執(zhí)行優(yōu)先級(jí)的線程。此函數(shù)用來變更由dispatch_queue_creat函數(shù)生成的Dispatch Queue的優(yōu)先級(jí)
//mySerialDispatchQueue為默認(rèn)優(yōu)先級(jí)經(jīng)過dispatch_set_target_queue函數(shù)將其設(shè)置為后臺(tái)優(yōu)先級(jí)
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("gcd.MySerialDispatchQueue", DISPATCH_QUEUE_SERIAL
);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);
dispatch_set_target_queue將第一個(gè)參數(shù)的優(yōu)先級(jí)變?yōu)榈诙€(gè)參數(shù)的優(yōu)先級(jí)
3.dispatch_after(延遲處理)
dispatch_after 并不是在指定時(shí)間后執(zhí)行處理,只是在指定時(shí)間追加處理到Dispatch Queue。
dispatch_after(<#dispatch_time_t when#>, <#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
參數(shù)一: 指定時(shí)間用的dispatch_time_t類型的值。該值使用dispatch_time函數(shù)或dispatch_walltime 函數(shù)得到。
參數(shù)二:指定要追加處理的Dispatch Queue
參數(shù)三:指定記述要執(zhí)行處理的Block
4. Dispatch Group(調(diào)度組)
作用:組中處理順序任意,但結(jié)束處理一定是在組中所有的處理都執(zhí)行結(jié)束后才執(zhí)行

5. dispatch_barrier_async(異步調(diào)度屏障)
該函數(shù)同dispatch_queue_creat函數(shù)生成的Concurrent Dispatch Queue一起使用
dispatch_barrier_async函數(shù)會(huì)等待追加到Concurrent Dispatch Queue上的并行執(zhí)行的處理全部結(jié)束后再將指定的處理追加到該Concurrent Dispatch Queue中,然后在由dispatch_barrier_async函數(shù)追加的執(zhí)行處理執(zhí)行完畢后,Concurrent Dispatch Queue才恢復(fù)為一般的動(dòng)作

注:可以用Dispatch Group和dispatch_set_target_queue來實(shí)現(xiàn),但代碼復(fù)雜
使用 dispatch_barrier_async和Concurrent Dispatch Queue函數(shù)可實(shí)現(xiàn)高效率的數(shù)據(jù)庫訪問和文件訪問。
6.dispatch_sync(同步調(diào)度)
dispatch_async函數(shù)的“async”意味著“非同步”(asynchronous),就是將指定的block“非同步”地追加到指定的Dispatch Queue中,diispatch_async函數(shù)不做任何等待。
dispatch_sync函數(shù)的“sync”意味著“同步”(synchronous),就是將指定的block“同步”地追加到指定的Dispatch Queue中。再追加block結(jié)束之前dispatch_sync函數(shù)會(huì)一直等待
發(fā)生死鎖的情況
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{NSLog(@“hello");});
該源代碼在Main Dispatch Queue 中執(zhí)行Block,并等待其執(zhí)行結(jié)束。而其實(shí)在主線程中正在執(zhí)行這些代碼,所以無法執(zhí)行追加到Main Dispatch Queue 的Block。
下面的例子也一樣。
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{NSLog(@“hello");});
Main Dispatch Queue 中執(zhí)行的Block 等待Main Dispatch Queue 中要執(zhí)行的Block執(zhí)行結(jié)束,造成死鎖。
Serial Dispatch Queue也會(huì)引起同樣的問題
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("gcd.MySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(mySerialDispatchQueue, ^{ NSLog(@"hello"); });
7.dispatch_apply
作用: 提交一個(gè)Block到一個(gè)分發(fā)隊(duì)列,以供多次調(diào)用。dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch Queue的關(guān)聯(lián)API。該函數(shù)按指定的Block追加到指定的Dispatch Queue中,并等待全部處理執(zhí)行結(jié)束。

因?yàn)樵贕lobal Dispatch Queue中執(zhí)行處理,所以各個(gè)處理的執(zhí)行時(shí)間不定。淡水輸出結(jié)果最終的done必定在最后位置上,因?yàn)閐ispatch_apply函數(shù)會(huì)等待全部處理執(zhí)行結(jié)束。
參數(shù)說明:
參數(shù)一:Block的重復(fù)次數(shù)
參數(shù)二:追加對(duì)象的Dispatch Queue
參數(shù)三:帶有參數(shù)的Block,為了按照第一個(gè)參數(shù)重復(fù)追加Block并區(qū)分各個(gè)Block而使用
由于dispatch_apply函數(shù)與dispatch_sync函數(shù)相同,會(huì)等待處理執(zhí)行結(jié)束,因此推薦在dispatch_async函數(shù)中非同步地執(zhí)行dispatch_apply函數(shù)

8.dispatch_suspend/dispatch_resume
dispatch_suspend函數(shù)掛起指定的Dispatch Queue;
dispatch_resume函數(shù)恢復(fù)指定的Dispatch Queue;
注:這些函數(shù)對(duì)已經(jīng)執(zhí)行的處理沒有影響。掛起后追加到Dispatch Queue中但尚未執(zhí)行的處理在此之后停止執(zhí)行。而恢復(fù)則使這些處理能夠繼續(xù)執(zhí)行;
9.Dispatch Semaphore
信號(hào)量 Dispatch Semaphore是持有計(jì)數(shù)的信號(hào),該計(jì)數(shù)是多線程編程中的計(jì)數(shù)類型信號(hào)。計(jì)數(shù)為0時(shí)等待,大于等于一時(shí)減去一而不等待;

dispatch_semaphore_wait函數(shù)等待計(jì)數(shù)值達(dá)到大于等于1
參數(shù)1:為信號(hào)量
參數(shù)2:由dispatch_time_t函數(shù)指定的等待時(shí)間;
該函數(shù)的返回值與dispatch_group_wait函數(shù)相同可通過返回值進(jìn)行分支處理

注:
在沒有Serial Dispatch Queue 和dispatch_barrier_async函數(shù)那么大粒度且一部分需要進(jìn)行排他控制的情況下,Dispatch Semaphore便可發(fā)揮威力
10.dispatch_once
dispatch_once函數(shù)是保證在應(yīng)用程序中只執(zhí)行一次指定處理的API。
通常在單例模式中用到

11.Dispatch I/O
作用: 使用多個(gè)線程更快的并列讀取
- dispatch_io_creat函數(shù)用來生成Dispatch I/O,并指定發(fā)生錯(cuò)誤時(shí)使用的Block
- dispatch_io_set_low_water設(shè)定一次讀取的大?。ǚ指畲笮。?/li>
- dispatch_io_read函數(shù)使用Global Dispatch Queue 開始并列讀取。每當(dāng)各個(gè)分割的文件塊讀取結(jié)束時(shí),將含有文件款數(shù)據(jù)的 DispatchData傳遞給dispatch_io_read函數(shù)指定的讀取結(jié)束時(shí)回調(diào)用的Block?;卣{(diào)用的Block分析傳遞過來的Dispatch Data 并進(jìn)行結(jié)合處理
注:如果想提高文件讀取速度,可以嘗試使用Dispatch I/O
二、GCD的實(shí)現(xiàn)
1.要點(diǎn):
1. IOS和OS X的核心是XNU內(nèi)核,GCD是基于XUN內(nèi)核實(shí)現(xiàn)的
2 .GCD的API全部在libdispatch庫中
3 .GCD的底層實(shí)現(xiàn)主要有Dispatch Queue 和Dispatch Source
Dispatch Queue :管理block操作
Dispatch Source :處理事件(比如線程間通信)
2.Dispatch Queue 實(shí)現(xiàn)
Dispatch Queue 對(duì)于我們開發(fā)者來說應(yīng)該是非常熟悉了,運(yùn)用的場(chǎng)景非常之多,但是他的內(nèi)部是如何實(shí)現(xiàn)的呢?
- 用于管理追加的Block的C語言層實(shí)現(xiàn)的FIFO隊(duì)列
- Atomic函數(shù)中實(shí)現(xiàn)的用于排他控制的輕量級(jí)信號(hào)
- 用于管理線程的C語言層實(shí)現(xiàn)的一些容器
不難想象,GCD的實(shí)現(xiàn)需要使用以上這些工具,但是如果僅用這些內(nèi)容便可實(shí)現(xiàn),那么就不需要內(nèi)核級(jí)實(shí)現(xiàn)了。(實(shí)際上在一般的Linux內(nèi)核中可能使用面向Linux操作系統(tǒng)而移植的GCD)。
甚至有人會(huì)想,只要努力編寫線程管理的代碼,就根本用不到GCD,是這樣的嗎?
我們先來回顧一下蘋果的官方說明:
通常,應(yīng)用程序中編寫的線程管理用的代碼要在系統(tǒng)級(jí)實(shí)現(xiàn)。
實(shí)際上正如這句話所說,在系統(tǒng)級(jí)即iOS和OS X的核心XNU內(nèi)核級(jí)上實(shí)現(xiàn),因此無論編程人員如何努力編寫管理線程的代碼,在性能方面也不可能勝過XNU內(nèi)核級(jí)所實(shí)現(xiàn)的GCD。
使用GCD要比使用pthreads和NSThread這些一般的多線程編程API更好。并且,如果用GCD就不必編寫為操作線程反復(fù)出現(xiàn)的類似的源代碼(這里被稱為固定源代碼片段),而可以在線程中集中實(shí)現(xiàn)處理內(nèi)容,真的是好處多多。我們盡量多使用GCD或者使用了Cocoa框架GCD的NSOperationQueue類等API。
那么首先確認(rèn)一下用于實(shí)現(xiàn)Dispatch Queue 而使用的軟件組件。如表所示:
| 組件名稱 | 提供技術(shù) |
|---|---|
| libdispatch | Dispatch Queue |
| Libc(pthreads) | pthread_workqueue |
| XNU內(nèi)核 | workqueue |
- 編程人員所使用GCD的API全部包含在libdispatch庫的C語言函數(shù)。Dispatch Queue通過結(jié)構(gòu)體和鏈表,被實(shí)現(xiàn)為FIFO隊(duì)列。FIFO隊(duì)列主要是負(fù)責(zé)管理通過dispatch_async等函數(shù)所追加的一系列Blocks??梢岳斫鉃橐坏┪覀?cè)诔绦蛑杏缮系较伦芳恿艘唤MBlocks,那么排除掉dispatch_after,其內(nèi)部的追加過程是一個(gè)先進(jìn)先出原則。
- 但是Block本身并不是直接加入到這個(gè)FIFO隊(duì)列中,而是先加入Dispatch Continuation這一dispatch_continuation_t類型結(jié)構(gòu)體中,然后再進(jìn)入FIFO隊(duì)列。該結(jié)構(gòu)體用于記憶Block所屬的Dispatch Group和其他一些信息,相當(dāng)于一般常說的執(zhí)行上下文(execution context)。
- Dispatch Queue可通過dipatch_set_target_queue函數(shù)設(shè)定,可以設(shè)定執(zhí)行該Dispatch Queue處理的Dispatch Queue為目標(biāo)。該目標(biāo)可像串珠子一樣,設(shè)定多個(gè)連接在一起的Dispatch Queue,但是在連接串的最后必須設(shè)定為Main Dispatch Queue,或各種優(yōu)先級(jí)的Global Dispatch Queue,或是準(zhǔn)備用于Serial Dispatch Queue的各種優(yōu)先級(jí)的Global Dispatch Queue。
Main Dispatch Queue 在RunLoop中執(zhí)行Block。
Global Dispatch Queue有如下8種:
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)
注意前面四種 和后面四種不同優(yōu)先級(jí)的Queue有一詞之差:Overcommit。
其區(qū)別就在于 Overcommit Queue不管系統(tǒng)狀態(tài)如何都會(huì)強(qiáng)制生成線程隊(duì)列。
這8種Global Dispatch Queue 各使用一個(gè)pthread_workqueue。GCD初始化時(shí),使用pthread_workqueue_create_np函數(shù)生成pthread_wrokqueue。
pthread_wrokqueue包含在Libc提供的pthreads API中。它通過系統(tǒng)的bsdthread_register和workq_open函數(shù)調(diào)用,在初始化XNU內(nèi)核的workqueue之后獲取其信息。
XNU內(nèi)核有4種workqueue:
WORKQUEUE_HIGH_PRIOQUEUE
WORKQUEUE_DEFAULT_PRIOQUEUE
WORKQUEUE_LOW_PRIOQUEUE
WORKQUEUE_BG_PRIOQUEUE
以上為4種執(zhí)行優(yōu)先級(jí)的workqueue。該執(zhí)行優(yōu)先級(jí)與Global Dispatch Queue的4種執(zhí)行優(yōu)先級(jí)相同。
Dispatch Queue中執(zhí)行Block的過程:

① 當(dāng)在Global Dispatch Queue 中執(zhí)行Block時(shí),libdispatch 從Global Dispatch Queue自身的FIFO隊(duì)列取出Dispatch Continuation,調(diào)用pthread_workqueue_additem_np函數(shù)。將該Global Dispatch Queue 本身、符合其優(yōu)先級(jí)的workqueue信息以及執(zhí)行Dispatch Continuation的回調(diào)函數(shù)等傳遞給參數(shù)。
② pthread_workqueue_additem_np函數(shù)使用workq_kernreturn系統(tǒng)調(diào)用,通知workqueue增加應(yīng)當(dāng)執(zhí)行的項(xiàng)目。根據(jù)該通知,XNU內(nèi)核基于系統(tǒng)狀態(tài)判斷是否要生成線程。如果是Overcommit優(yōu)先級(jí)的Global Dispatch Queue ,workqueue則始終生成線程。
注:該線程雖然與iOS和OS X中通常使用的線程大致相同,但是有一部分pthread API不能使用。詳細(xì)信息可以參考蘋果的官方文檔《并發(fā)編程指南》的“Compatibility with POSIX Threads“這一章節(jié)。
另外,因?yàn)閣orkqueue生成的線程在實(shí)現(xiàn)用于workqueue的線程計(jì)劃表中運(yùn)行,他的上下文切換(shift context)與普通的線程有很大的不同。這也是我們使用GCD的原因。
③ workqueue的線程執(zhí)行pthread_workqueue函數(shù),該函數(shù)調(diào)用libdispatch的回調(diào)函數(shù)。在該回調(diào)函數(shù)中執(zhí)行加入到Global Dispatch Queue中的下一個(gè)Block。
以上就是Dispatch Queue執(zhí)行的大概過程。
由此可知,在編程人員自己編寫的線程管理代碼中想發(fā)揮出原生GCD的性能是不可能的。
3.Dipatch Source
GCD中除了主要的Dispatch Queue以外,還有不太引人注目的Dispatch Source。它是一種BSD系列內(nèi)核慣有功能kqueue的封裝。
kqueue是在XNU內(nèi)核中由發(fā)生各種事件時(shí),在應(yīng)用程序?qū)用鎭硖幚淼募夹g(shù)。其CPU符合非常小,基本上不占用資源。kqueue可以說是應(yīng)用程序處理XNU內(nèi)核中的事件處理機(jī)制中最為優(yōu)秀的。
Dispatch Source 可以處理以下事件。如表所示:
Dispatch Source的種類

事件發(fā)生時(shí),在指定的Dispatch Queue中可執(zhí)行事件的處理。
注:
- Dispatch Queue沒有“取消”這一概念。一旦將處理追加到Dispatch Queue中,就沒有辦法將該處理去除,也沒有辦法可以在執(zhí)行中取消處理。編程人員要么在處理中導(dǎo)入取消這一概念,要么放棄取消,或者使用NSOperationQueue等其他方法
- Dispatch Source 與Dispatch Queue不同,是可以取消的。而且取消時(shí)必須執(zhí)行的處理可指定為回調(diào)用的Block形式。因此使用Dispatch Source 實(shí)現(xiàn)XNU內(nèi)核中發(fā)生的事件處理要比直接使用kqueue實(shí)現(xiàn)更為簡(jiǎn)單。