??說來慚愧,學(xué)習(xí)iOS那么久,關(guān)于GCD都是直接看網(wǎng)上的資料,還沒有系統(tǒng)的看過官方文檔,所以打算好好讀一讀官方文檔,加深自己對GCD的了解。
什么是GCD
什么是GCD呢?
從《Objective-C高級編程 iOS與OS X多線程和內(nèi)存管理》一書中這樣介紹的:
Grand Central Dispatch(GCD)是異步執(zhí)行任務(wù)的技術(shù)之一。開發(fā)者只需要定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue 中, GCD就能生成必要的線程并計劃執(zhí)行任務(wù)。由于線程管理是作為系統(tǒng)的一部分來實現(xiàn)的,因此可統(tǒng)一管理,也可執(zhí)行任務(wù),這樣久比以前的線程更有效率。也就是說,GCD用我們難以置信的非常簡潔技術(shù)方法,實現(xiàn)了極為復(fù)雜繁瑣的多線程編程。
但是我在看官方文檔中并沒有看到這段話,估計是更新了吧。最新的官網(wǎng)上是這么說的:
Grand Central Dispatch(GCD)包含了用來增強系統(tǒng)對多核硬件的并發(fā)執(zhí)行能力的語言特性、runtime庫和系統(tǒng)加強包。GCD在系統(tǒng)級別上運行,可以更好的適應(yīng)所有運行的應(yīng)用程序的需求,并以一種平衡的方式將它們和可用的資源相匹配。
總體來說,GCD使用方便且高效,但是使用的時候需要注意,避免線程阻塞、死鎖等狀況發(fā)生。
GCD的API
1.Dispatch Queue
Dispatch Queue 是一個輕量級的對象,你只需提交你想執(zhí)行任務(wù)的代碼塊。Dispatch Queue 按照FIFO(先進先出)順序調(diào)用提交給它的代碼塊。一個串型隊列一次只能調(diào)用一個塊,但是不同的隊列可以同時調(diào)用它們自己的代碼塊。
Dispatch Queue 的類型分為兩種:DISPATCH_QUEUE_SERIAL 和 DISPATCH_QUEUE_CONCURRENT。
| Dispatch Queue Type | 說明 |
|---|---|
| DISPATCH_QUEUE_SERIAL | 串型隊列,每次只能執(zhí)行一個任務(wù) |
| DISPATCH_QUEUE_CONCURRENT | 并行隊列,可以同時執(zhí)行多個任務(wù) |
2.獲得Dispatch Queue
我們了解了Dispatch Queue的類型,那么如何獲得DispatchQueue呢?我們有兩張方法獲取Dispatch Queue:1.通過dispatch_queue_create 自己創(chuàng)建 2.獲取系統(tǒng)提供的Dispatch Queue(Main Dispatch Queue & Global Dispatch Queue)。
2.1 dispatch_queue_create
我們可以直接通過dispatch_queue_create函數(shù)生成Dispatch Queue
//第一個參數(shù):queue的唯一標(biāo)識 ;第二個參數(shù):隊列的類型 ,為NULL是默認為DISPATCH_QUEUE_SERIAL
dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)
dispatch_queue_t queue = dispatch_queue_create("com.serial0.myqueue", NULL);
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial1.myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrentQueue.myqueue", DISPATCH_QUEUE_CONCURRENT);
其中dispatch_queue_create函數(shù)的第一個參數(shù)是創(chuàng)建的隊列的唯一標(biāo)識,官方文檔推薦我們使用反向dns的命名格式(com.examle.myqueue)。你可以將標(biāo)識設(shè)置為NULL,但是不推薦,因為給隊列添加標(biāo)識,能夠在調(diào)試的時候清楚的知道當(dāng)前的執(zhí)行隊列,還能在crashLog中給出提示。 你也可以通過dispatch_queue_get_label函數(shù)獲取當(dāng)前隊列的標(biāo)識。第二個參事設(shè)置隊列的類型,置NULL為DISPATCH_QUEUE_SERIAL類型。
2.2 Main Dispatch Queue & Global Dispatch Queue
Main Dispatch Queue 為主線程,有且僅有一個,所以主線程是serial Dispatch Queue。追加到主線程到任務(wù)在主線程的RunLoop中執(zhí)行,所以用戶界面的UI更新這些必須放到主線程中,而一些耗時的操作盡量避免放到主線程。
Global Dispatch Queue 為全局線程,是Concurrent Dispatch Queue,有四個優(yōu)先級,分別為:
- 高優(yōu)先級(DISPATCH_QUEUE_PRIORITY_HIGH)
- 默認優(yōu)先級(DISPATCH_QUEUE_PRIORITY_DEFAULT)
- 低優(yōu)先級(DISPATCH_QUEUE_PRIORITY_LOW)
- 后臺優(yōu)先級(DISPATCH_QUEUE_PRIORITY_BACKGROUND)
系統(tǒng)提供的Dispatch Queue的總結(jié)如下:
| 名稱 | 類型 | 說明 |
|---|---|---|
| Main Dispatch Queue | Serial Dispatch Queue | 主線程執(zhí)行 |
| Global Dispatch Queue(High Priority) | Concurrent Dispatch Queue | 高優(yōu)先級 |
| Global Dispatch Queue(Default Priority) | Concurrent Dispatch Queue | 默認優(yōu)先級 |
| Global Dispatch Queue(Low Priority) | Concurrent Dispatch Queue | 低優(yōu)先級 |
| Global Dispatch Queue(Background Priority) | Concurrent Dispatch Queue | 后臺優(yōu)先級 |
系統(tǒng)Dispatch Queue 獲取如下:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
GCD使用
我們了解了GCD,也知道獲取Dispatch Queue 的方法,那么如何使用呢?系統(tǒng)的API里有許多方法,這里先介紹最常見的兩種方法dispatch_async和dispatch_sync。
dispatch_async 異步執(zhí)行
異步執(zhí)行,不受其他代碼塊的影響,一旦放入隊列中就執(zhí)行。
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(defaultQueue, ^{
NSLog(@"actionA:%@", [NSThread currentThread]);
});
dispatch_async(defaultQueue, ^{
NSLog(@"actionB:%@", [NSThread currentThread]);
});
dispatch_async(defaultQueue, ^{
NSLog(@"actionC:%@", [NSThread currentThread]);
});
dispatch_async(defaultQueue, ^{
NSLog(@"actionD:%@", [NSThread currentThread]);
});
代碼執(zhí)行結(jié)果如下:
2018-03-22 15:48:10.332231+0800 LCChat[93644:7440393] actionC:<NSThread: 0x60c000279f40>{number = 5, name = (null)}
2018-03-22 15:48:10.332232+0800 LCChat[93644:7440394] actionA:<NSThread: 0x60c000279bc0>{number = 3, name = (null)}
2018-03-22 15:48:10.332242+0800 LCChat[93644:7440395] actionD:<NSThread: 0x604000276c80>{number = 6, name = (null)}
2018-03-22 15:48:10.332252+0800 LCChat[93644:7440397] actionB:<NSThread: 0x608000271040>{number = 4, name = (null)}
說明異步執(zhí)行的代碼塊順序是不可控制的。
dispatch_sync 同步執(zhí)行
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(defaultQueue, ^{
NSLog(@"actionA:%@", [NSThread currentThread]);
});
dispatch_sync(defaultQueue, ^{
NSLog(@"actionB:%@", [NSThread currentThread]);
});
dispatch_sync(defaultQueue, ^{
NSLog(@"actionC:%@", [NSThread currentThread]);
});
dispatch_sync(defaultQueue, ^{
NSLog(@"actionD:%@", [NSThread currentThread]);
});
代碼執(zhí)行的結(jié)果如下:
2018-03-22 15:51:18.971098+0800 LCChat[93697:7443960] actionA:<NSThread: 0x60800006cd80>{number = 1, name = main}
2018-03-22 15:51:18.971344+0800 LCChat[93697:7443960] actionB:<NSThread: 0x60800006cd80>{number = 1, name = main}
2018-03-22 15:51:18.971475+0800 LCChat[93697:7443960] actionC:<NSThread: 0x60800006cd80>{number = 1, name = main}
2018-03-22 15:51:18.971603+0800 LCChat[93697:7443960] actionD:<NSThread: 0x60800006cd80>{number = 1, name = main}
證明同步執(zhí)行遵守FIFO,必須等前一個block完成才能進行下一個block。
總結(jié)
基本的GCD的介紹和常用的GCD使用在上文中已經(jīng)大致的介紹了,其實你看官方文檔,你會發(fā)現(xiàn)GCD還有很多強大的功能,比如dispatch_once,dispatch I/Od等,這些不常用的,等有空的時候再一一細讀。