前言
什么是GCD?
Grand Central dispatch(GCD)是異步執(zhí)行任務(wù)的技術(shù)之一。一般將應(yīng)用程序中記述的線程管理用的代碼在系統(tǒng)級(jí)中實(shí)現(xiàn)。開發(fā)者只需要定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)膁ispatch Queue中,GCD就能生成必要的線程并計(jì)劃執(zhí)行任務(wù)。由于線程管理是作為系統(tǒng)的一部分來實(shí)現(xiàn)的,因此可以統(tǒng)一管理,也可執(zhí)行任務(wù),這樣就比以前的線程更加有效率。
什么是線程?
- 線程(thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。
- Mac、iPhone的操作系統(tǒng)OS X、iOS根據(jù)用戶的指示啟動(dòng)應(yīng)用程序后,首先便將包含在應(yīng)用程序中的CPU命令列配置到內(nèi)存中。CPU從應(yīng)用程序指定的地址開始,一個(gè)一個(gè)地執(zhí)行CPU命令列。由于一個(gè)CPU一次只能執(zhí)行一個(gè)命令,不能執(zhí)行某處分開的并列的兩個(gè)命令,因此通過CPU執(zhí)行的CPU命令列就是一條路走到黑。這里所說的“1個(gè)CPU執(zhí)行的CPU名列為一條路走到黑”就是“線程”。
- 現(xiàn)在一個(gè)物理的CPU芯片實(shí)際上有64(64核)個(gè)CPU,即可以擁有多條線程。
- 多線程編程會(huì)產(chǎn)生很多編程技術(shù)問題:數(shù)據(jù)競(jìng)爭(zhēng)、死鎖、內(nèi)存消耗等問題。
- 想要更加了解線程、進(jìn)程等可自行參閱《操作系統(tǒng)原理》這本書。
dispatch Queue
dispatch Queue是執(zhí)行處理的等待隊(duì)列。我們可以調(diào)用dispatch_async函數(shù)等API,在Block語法中記述想執(zhí)行的處理并追加到dispatch Queue中。dispatch Queue會(huì)按照FIFO(先進(jìn)先出)執(zhí)行處理。下面的圖解釋了FIFO

在dispatch Queue中存在兩種dispatch Queue,一種是等待現(xiàn)在執(zhí)行中的Serial dispatch Queue,另一種是不等待直接執(zhí)行的Concurrent dispatch Queue。
| dispatch Queue種類 | 說明 |
|---|---|
| Serial dispatch Queue | 等待現(xiàn)在執(zhí)行中的處理結(jié)束后才開始執(zhí)行 |
| Concurrent dispatch Queue | 不等待現(xiàn)在執(zhí)行中的處理,直接開始執(zhí)行 |


因?yàn)镾erial dispatch Queue是等待執(zhí)行完成后才開始下一個(gè)處理。那么在有多個(gè)處理的時(shí)候,也是按照先后順序來的。
int main(int argc, const char * argv[]) {
// 創(chuàng)建一個(gè)SerialQueue
dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
// Serial dispatch Queue
dispatch_async(serialdispatchQueue, ^{printf("1\n");});
dispatch_async(serialdispatchQueue, ^{printf("2\n");});
dispatch_async(serialdispatchQueue, ^{printf("3\n");});
dispatch_async(serialdispatchQueue, ^{printf("4\n");});
// 防止程序結(jié)束
while (1) {
}
return 0;
}
執(zhí)行結(jié)果如下:

Concurrent dispatch Queue是不等待執(zhí)行,那么任何任務(wù)添加到Concurrent dispatch Queue后,就會(huì)立即執(zhí)行。但是Concurrent dispatch Queue不是無限制的立即執(zhí)行當(dāng)前添加的處理,當(dāng)前并行執(zhí)行的處理的數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài)。即iOS和OS X基于dispatch Queue中的處理數(shù)、CPU數(shù)、以及CPU負(fù)荷等當(dāng)前系統(tǒng)狀態(tài)來決定Concurrent dispatch Queue中并行處理數(shù)。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
// 創(chuàng)建一個(gè)Concurrent dispatch Queue
dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
// Serial dispatch Queue
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"1");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"2");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"3");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"4");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"5");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"6");});
// 防止程序結(jié)束
while (1) {
}
return 0;
}
執(zhí)行結(jié)果:

其中執(zhí)行結(jié)果亂序是因?yàn)樘幚硖砑拥骄€程中的時(shí)候,需要等待線程執(zhí)行完成后,才開始執(zhí)行。即6個(gè)Block添加到6個(gè)線程中,6個(gè)線程里面都仍然有任務(wù),等任務(wù)執(zhí)行完成后,才開始執(zhí)行我們添加的處理。
dispatch_queue_create
在Dispatch Queue中,我們使用了dispatch_queue_create來建立一個(gè)Queue。
生成Dispatch Queue有兩種方法:
- 通過GCD的API生成dispatch Queue
- 獲取系統(tǒng)標(biāo)準(zhǔn)提供的dispatch Queue
這里先說明第一種方法,第二種在Main dispatch Queue/Global dispatch Queue中說明。
使用dispatch_queue_create
// 創(chuàng)建一個(gè)Concurrent dispatch Queue
dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
// 創(chuàng)建一個(gè)Serial dispatch Queue
dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
dispatch_queue_create函數(shù)有2個(gè)參數(shù),第一個(gè)參數(shù)是dispatch Queue的名稱,推薦使用應(yīng)用程序ID這樣的逆序全程域名。該名稱在Xcode和Instruments的調(diào)試器中作為dispatch Queue的名稱表示。第二個(gè)參數(shù)是指定dispatch Queue的類型,如果填寫NULL則默認(rèn)為DISPATCH_QUEUE_SERIAL。
前文講到,Serial dispatch Queue同時(shí)只能執(zhí)行一個(gè)處理。但是生成多個(gè)Serial dispatch Queue時(shí),各個(gè)Serial dispatch Queue將并行執(zhí)行。4個(gè)處理追加到4個(gè)Serial dispatch Queue中,4個(gè)處理將同時(shí)執(zhí)行。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
// 創(chuàng)建一個(gè)SerialQueue
dispatch_queue_t serialdispatchQueue1 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialdispatchQueue2 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialdispatchQueue3 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
// Serial dispatch Queue
dispatch_async(serialdispatchQueue1, ^{NSLog(@"1");});
dispatch_async(serialdispatchQueue2, ^{NSLog(@"2");});
dispatch_async(serialdispatchQueue3, ^{NSLog(@"3");});
// 防止程序結(jié)束
while (1) {
}
return 0;
}
執(zhí)行結(jié)果:

由執(zhí)行結(jié)果亂序可以知道,多個(gè)Serial dispatch Queue是并行執(zhí)行的!
dispatch_queue_create使用注意
- 不能無限制的創(chuàng)建Serial Dispatch Queue,會(huì)消耗大量的內(nèi)存,引起大量的上下文切換,大幅度降低系統(tǒng)的響應(yīng)性能。
- 在為了避免多個(gè)線程對(duì)同一個(gè)資源進(jìn)行操作時(shí)(數(shù)據(jù)競(jìng)爭(zhēng))使用Serial Dispatch Queue,因?yàn)槠涫褂靡粋€(gè)線程,數(shù)據(jù)安全。
- 當(dāng)不需要顧忌數(shù)據(jù)競(jìng)爭(zhēng)問題時(shí)候,推薦使用Concurrent Dispatch Queue。因?yàn)椴还苌啥嗌?,系統(tǒng)會(huì)對(duì)其進(jìn)行管理,不用擔(dān)心Serial Dispatch Queue類似的問題。
- 最好為每一個(gè)Dispatch Queue編寫不同的名字,否則你會(huì)在調(diào)試多線程程序的時(shí)候感覺自己仿佛是一個(gè)辣雞。
- 關(guān)于
dispatch_retain和dispatch_release的使用- 如果你部署的最低目標(biāo)低于 iOS 6.0 or Mac OS X 10.8,你應(yīng)該自己管理GCD對(duì)象,使用(dispatch_retain,dispatch_release),ARC并不會(huì)去管理它們。
- 如果你部署的最低目標(biāo)是 iOS 6.0 or Mac OS X 10.8或者更高,ARC已經(jīng)能夠管理GCD對(duì)象了,這時(shí)候,GCD對(duì)象就如同普通的OC對(duì)象一樣,不應(yīng)該使用dispatch_retain或者dispatch_release。
Main Dispatch Queue/Global Dispatch Queue
除了使用dispatch_queue_create,還可以利用系統(tǒng)標(biāo)準(zhǔn)提供的Dispatch Queue。系統(tǒng)提供了2個(gè):
- Main Dispatch Queue,即主線程中執(zhí)行的Dispatch Queue,而主線程只有一個(gè),所以Main Dispatch Queue就是Serial Dispatch Queue.
- Global Dispatch Queue,是所有應(yīng)用程序都能夠使用的Concurrent Dispatch Queue。所以沒有必要通過
dispatch_queue_create來創(chuàng)建,直接獲取Global Dispatch Queue即可。
/** Main Dispatch Queue的獲取 */
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
/** Global Dispatch Queue(最高)的獲取方法 */
dispatch_queue_t highGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
/** Global Dispatch Queue(默認(rèn))的獲取方法 */
dispatch_queue_t defaultGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/** Global Dispatch Queue(低)的獲取方法 */
dispatch_queue_t losGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
/** Global Dispatch Queue(后臺(tái),最低)的獲取方法 */
dispatch_queue_t backgroundGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
Global Dispatch Queue
Global Dispatch Queue有4個(gè)優(yōu)先級(jí),分別是高優(yōu)先級(jí)、默認(rèn)優(yōu)先級(jí)、低優(yōu)先級(jí)和后臺(tái)優(yōu)先級(jí)。
| 名稱 | Dispatch Queue的種類 | 說明 |
|---|---|---|
| Main Dispatch Queue | Serial Queue | 主線程執(zhí)行 |
| Global Dispatch Queue(High Priority)高優(yōu)先級(jí) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級(jí):最高 |
| Global Dispatch Queue(Default Priority)默認(rèn)優(yōu)先級(jí) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級(jí):默認(rèn) |
| Global Dispatch Queue(Low Priority)低優(yōu)先級(jí) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級(jí):低 |
| Global Dispatch Queue(Background Priority)后臺(tái)優(yōu)先級(jí) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級(jí):后臺(tái)(最低) |
結(jié)語
- 本文主要講常用的一些GCD的API,更多內(nèi)容,請(qǐng)等待下一周更新。
- 此為《Objective-C 高級(jí)編程》的學(xué)習(xí)筆記。
- 如有錯(cuò)誤,歡迎指正。
- 如需轉(zhuǎn)載,請(qǐng)注明出處。