GCD(Grand central Dispatch)是Apple開發的一個多核編程的較新的解決方法。它主要用於優化應用程序以支持多核處理器以及其餘對稱多處理系統。下面我講講述關於GCD的點,通篇讀完大約10-20分鐘。程序員
1、爲何要用GCD?編程
GCD是iOS線程的一種,也是被常用的一種方式。GCD也有不少的好處:併發
(1)GCD可用於多核的並行運算;異步
(2)GCD會自動利用更多的CPU內核;async
(3)GCD會自動管理線程的生命週期;函數
(4)程序員只須要告訴GCD想要執行什麼任務,不須要編寫任何線程管理代碼優化
2、GCD任務和隊列spa
先了解GCD中兩個核心概念:任務和隊列。線程
1.任務:就是執行操做的意思,換句話說就是你在線程中執行的那段代碼。在GCD中是放在block中的。執行任務有兩種方式:同步執行(sync)和異步執行(async)。二者的主要區別是:是否等待隊列的任務執行結束,以及是否具有開啓新線程的能力。code
1).同步執行(sync):
同步添加到指定的隊列中,在添加的任務執行結束以前,會一直等待,知道隊列裏面的任務完成以後再繼續執行。
只能在當前線程中執行任務,不具有開啓新線程的能力。
2).異步執行(async):
異步添加任務到指定的隊列中,它不會作任何等待,能夠繼續執行任務。
能夠在新的線程中執行任務,具有開啓新線程的能力。
注意: 異步執行(async) 雖然具備開啓新線程的能力,可是並不必定開啓新線程。這跟任務所指定的隊列類型有關(下面會講)。
2.隊列:這裏的隊列指的是執行任務的等待隊列,即用來存聽任務的隊列。隊列是一種特殊的線性表,採用 FIFO(先進先出)的原則,即新任務老是被插入到隊列的末尾,而讀取任務的時候老是從隊列的頭部開始讀取。每讀取一個任務,則從隊列中釋放一個任務。
在GCD中有兩種隊列:串行隊列和併發隊列。二者都符合FIFO(先進先出)的原則。二者的主要區別是:執行的順序不一樣,以及開啓線程數不一樣。
1)串行隊列:
每次只有一個任務被執行。讓任務一個接着一個地執行。(只開啓一個線程,一個任務執行完畢後,再執行下一個任務)
2)併發隊列:
可讓多個任務併發(同時)執行。(能夠開啓多個線程,而且同時執行任務)
注意:併發隊列 的併發功能只有在異步(dispatch_async)函數下才有效
3、GCD的使用步驟
GCD的使用步驟其實比較簡單,只有兩步。
1.建立一個隊列(串行隊列或者並行隊列)
2.將任務追加到任務的等待隊列中,而後系統就會根據任務類型執行任務(同步執行或異步執行)
3.1隊列的建立方法/獲取方法
可使用dispatch_queue_create來建立隊列,須要傳入兩個參數,第一個參數表示隊列的惟一標識符,用於DEBUG,可爲空,Dispatch Queue的名稱推薦使用應用程序ID這種逆序全程域名,第二個參數用來表示串行隊列仍是併發隊列。DISPATCH_QUEUE_SERIAL 表示串行隊列,DISPATCH_QUEUE_CONCURRENT表示併發隊列。
下面是建立隊列的例子:
// 串行隊列的建立方法 dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL); // 併發隊列的建立方法 dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
對於串行隊列,GCD提供了一種特殊的串行隊列:主隊列(Main Dispatch Queue)。
1)全部放在主隊列的任務,都會放到主線程中執行。
2)可以使用dispatch_get_main_queue()得到主隊列。
// 主隊列的獲取方法 dispatch_queue_t queue = dispatch_get_main_queue();
對於併發隊列,GCD默認提供了全局併發隊列(Global Dispatch Queue)
可使用dispatch_get_global_queue
來獲取。須要傳入兩個參數。第一個參數是隊列優先級,通常用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個參數暫時沒用,能夠用0便可。
// 全局併發隊列的獲取方法 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3.2任務的建立方法
GCD提供了同步執行任務的建立方法dispatch_sync和異步執行任務建立方法dispatch_async。
// 同步執行任務建立方法 dispatch_sync(queue, ^{ // 這裏放同步執行任務代碼 }); // 異步執行任務建立方法 dispatch_async(queue, ^{ // 這裏放異步執行任務代碼 });
雖然使用GCD只須要兩步,可是既然咱們有兩種隊列(串行隊列/並行隊列),兩種任務執行方式(同步執行/異步執行)那麼咱們就有了四種不一樣的組合方式。這四種不一樣的方式爲:
1.同步執行 + 併發隊列
2.異步執行 + 併發隊列
3.同步執行 + 串行隊列
4.異步執行 + 串行隊列
實際上,剛纔說了兩種特殊隊列:全局併發隊列、主隊列。全局併發隊列能夠做爲普通併發隊列來使用,可是主隊列由於有點特殊,咱們就又多了兩種組合方式。咱們就有了六種不一樣的方式了。
5.同步執行 + 主隊列
6.異步執行 + 主隊列
那麼這幾種不一樣組合方式各有什麼區別呢,這裏爲了方便,先上結果,再來說解。
4、GCD的基本使用
4.1 同步執行+併發隊列
在當前線程中執行任務,不會開啓新線程,執行完一個任務,再執行下一個任務。
(void)syncConcurrent { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ // 追加任務1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_sync(queue, ^{ // 追加任務2 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_sync(queue, ^{ // 追加任務3 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程 } }); NSLog(@"syncConcurrent---end");
打印輸出結果:
輸出結果: 2018-02-23 20:34:55.095932+0800 YSC-GCD-demo[19892:4996930] currentThread---{number = 1, name = main} 2018-04-23 20:34:55.096086+0800 YSC-GCD-demo[19892:4996930] syncConcurrent---begin 2018-04-23 20:34:57.097589+0800 YSC-GCD-demo[19892:4996930] 1---{number = 1, name = main} 2018-04-23 20:34:59.099100+0800 YSC-GCD-demo[19892:4996930] 1---{number = 1, name = main} 2018-04-23 20:35:01.099843+0800 YSC-GCD-demo[19892:4996930] 2---{number = 1, name = main} 2018-04-23 20:35:03.101171+0800 YSC-GCD-demo[19892:4996930] 2---{number = 1, name = main} 2018-04-23 20:35:05.101750+0800 YSC-GCD-demo[19892:4996930] 3---{number = 1, name = main} 2018-04-23 20:35:07.102414+0800 YSC-GCD-demo[19892:4996930] 3---{number = 1, name = main} 2018-04-23 20:35:07.102575+0800 YSC-GCD-demo[19892:4996930] syncConcurrent---end
從結果能夠看到:
(1)全部任務都是在當前線程(主線程)中執行的,沒有開啓新的線程(同步執行不具有開啓新線程的能力)。
(2)全部任務都在打印的syncConcurrent---begin和syncConcurrent---end之間執行的(同步任務須要等待隊列的任務執行結束)。
(3)任務按順序執行的。按順序執行的緣由:雖然併發隊列能夠開啓多個線程,而且同時執行多個任務。可是由於自己不能建立新線程,只有當前線程這一個線程(同步任務不具有開啓新線程的能力),因此也就不存在併發。並且當前線程只有等待當前隊列中正在執行的任務執行完畢以後,才能繼續接着執行下面的操做(同步任務須要等待隊列的任務執行結束)。因此任務只能一個接一個按順序執行,不能同時被執行。
4.2 異步執行+併發隊列
能夠開啓多個線程,任務交替(同時)執行
- (void)asyncConcurrent { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 NSLog(@"asyncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ // 追加任務1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_async(queue, ^{ // 追加任務2 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_async(queue, ^{ // 追加任務3 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程 } }); NSLog(@"asyncConcurrent---end"); }
結果以下:
輸出結果: 2018-02-23 20:36:41.769269+0800 YSC-GCD-demo[19929:5005237] currentThread---{number = 1, name = main} 2018-04-23 20:36:41.769496+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent---begin 2018-04-23 20:36:41.769725+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent---end 2018-04-23 20:36:43.774442+0800 YSC-GCD-demo[19929:5005566] 2---{number = 5, name = (null)} 2018-04-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005567] 3---{number = 4, name = (null)} 2018-04-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005565] 1---{number = 3, name = (null)} 2018-04-23 20:36:45.779286+0800 YSC-GCD-demo[19929:5005567] 3---{number = 4, name = (null)} 2018-04-23 20:36:45.779302+0800 YSC-GCD-demo[19929:5005565] 1---{number = 3, name = (null)} 2018-04-23 20:36:45.779286+0800 YSC-GCD-demo[19929:5005566] 2---{number = 5, name = (null)}
在異步執行+併發隊列能夠看出:
除了當前線程(主線程),系統又開啓了3個線程,而且任務是交替/同時執行的。(異步執行具有開啓新線程的能力。且併發隊列可開啓多個線程,同時執行多個任務)。全部任務是在打印的syncConcurrent---begin和syncConcurrent---end以後才執行的。說明當前線程沒有等待,而是直接開啓了新線程,在新線程中執行任務(異步執行不作等待,能夠繼續執行任務)。
4.3 同步執行 + 串行隊列
不會開啓新線程,在當前線程執行任務。任務是串行的,執行完一個任務,再執行下一個任務。
- (void)syncSerial { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 NSLog(@"syncSerial---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ // 追加任務1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_sync(queue, ^{ // 追加任務2 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_sync(queue, ^{ // 追加任務3 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程 } }); NSLog(@"syncSerial---end"); }
輸出結果:
2018-02-23 20:39:37.876811+0800 YSC-GCD-demo[19975:5017162] currentThread---{number = 1, name = main} 2018-04-23 20:39:37.876998+0800 YSC-GCD-demo[19975:5017162] syncSerial---begin 2018-04-23 20:39:39.878316+0800 YSC-GCD-demo[19975:5017162] 1---{number = 1, name = main} 2018-04-23 20:39:41.879829+0800 YSC-GCD-demo[19975:5017162] 1---{number = 1, name = main} 2018-04-23 20:39:43.880660+0800 YSC-GCD-demo[19975:5017162] 2---{number = 1, name = main} 2018-04-23 20:39:45.881265+0800 YSC-GCD-demo[19975:5017162] 2---{number = 1, name = main} 2018-04-23 20:39:47.882257+0800 YSC-GCD-demo[19975:5017162] 3---{number = 1, name = main} 2018-04-23 20:39:49.883008+0800 YSC-GCD-demo[19975:5017162] 3---{number = 1, name = main} 2018-04-23 20:39:49.883253+0800 YSC-GCD-demo[19975:5017162] syncSerial---end
在同步執行+串行隊列能夠看出
(1)全部任務都是在當前線程(主線程)中執行的,並無開啓新的線程(同步執行不具有開啓新線程的能力)。
(2)全部任務都在打印的syncConcurrent---begin和syncConcurrent---end之間執行(同步任務須要等待隊列的任務執行結束)。
(3)任務是按順序執行的(串行隊列每次只有一個任務被執行,任務一個接一個按順序執行)。
4.4 異步執行 + 串行隊列
會開啓新線程,可是由於任務是串行的,執行完一個任務,再執行下一個任務
- (void)asyncSerial { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 NSLog(@"asyncSerial---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ // 追加任務1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_async(queue, ^{ // 追加任務2 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_async(queue, ^{ // 追加任務3 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程 } }); NSLog(@"asyncSerial---end"); }
執行結果以下:
2018-04-23 20:41:17.029999+0800 YSC-GCD-demo[20008:5024757] currentThread---{number = 1, name = main} 2018-04-23 20:41:17.030212+0800 YSC-GCD-demo[20008:5024757] asyncSerial---begin 2018-04-23 20:41:17.030364+0800 YSC-GCD-demo[20008:5024757] asyncSerial---end 2018-04-23 20:41:19.035379+0800 YSC-GCD-demo[20008:5024950] 1---{number = 3, name = (null)} 2018-04-23 20:41:21.037140+0800 YSC-GCD-demo[20008:5024950] 1---{number = 3, name = (null)} 2018-04-23 20:41:23.042220+0800 YSC-GCD-demo[20008:5024950] 2---{number = 3, name = (null)} 2018-04-23 20:41:25.042971+0800 YSC-GCD-demo[20008:5024950] 2---{number = 3, name = (null)} 2018-04-23 20:41:27.047690+0800 YSC-GCD-demo[20008:5024950] 3---{number = 3, name = (null)} 2018-04-23 20:41:29.052327+0800 YSC-GCD-demo[20008:5024950] 3---{number = 3, name = (null)}
從執行結果看:
(1)開啓了一條新線程(異步執行具有開啓新線程的能力,串行隊列只開啓一個線程)。
(2)全部任務是在打印的syncConcurrent---begin和syncConcurrent---end以後纔開始執行的(異步執行不會作任何等待,能夠繼續執行任務)。
(3)任務是按順序執行的(串行隊列每次只有一個任務被執行,任務一個接一個按順序執行)。
下邊講講剛纔咱們提到過的特殊隊列:主隊列。
主隊列:GCD自帶的一種特殊的串行隊列
全部放在主隊列中的任務,都會放到主線程中執行
可以使用dispatch_get_main_queue()得到主隊列
咱們再來看看主隊列的兩種組合方式。
4.5 同步執行 + 主隊列
同步執行 + 主隊列在不一樣線程中調用結果也是不同,在主線程中調用會出現死鎖,而在其餘線程中則不會。
/** * 同步執行 + 主隊列 * 特色(主線程調用):互等卡主不執行。 * 特色(其餘線程調用):不會開啓新線程,執行完一個任務,再執行下一個任務。 */ - (void)syncMain { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 NSLog(@"syncMain---begin"); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ // 追加任務1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_sync(queue, ^{ // 追加任務2 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_sync(queue, ^{ // 追加任務3 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程 } }); NSLog(@"syncMain---end"); }
輸出結果:
2018-02-23 20:42:36.842892+0800 YSC-GCD-demo[20041:5030982] currentThread---{number = 1, name = main} 2018-02-23 20:42:36.843050+0800 YSC-GCD-demo[20041:5030982] syncMain---begin (lldb)
在同步執行 + 主隊列能夠驚奇的發現:
在主線程中使用同步執行 + 主隊列,追加到主線程的任務一、任務二、任務3都再也不執行了,並且syncMain---end也沒有打印,在XCode 9上還會報崩潰。這是爲何呢?
這是由於咱們在主線程中執行syncMain方法,至關於把syncMain任務放到了主線程的隊列中。而同步執行會等待當前隊列中的任務執行完畢,纔會接着執行。那麼當咱們把任務1追加到主隊列中,任務1就在等待主線程處理完syncMain任務。而syncMain任務須要等待任務1執行完畢,才能接着執行。
那麼,如今的狀況就是syncMain任務和任務1都在等對方執行完畢。這樣你們互相等待,因此就卡住了,因此咱們的任務執行不了,並且syncMain---end也沒有打印。
要是若是不在主線程中調用,而在其餘線程中調用會如何呢
4.5.2 在其餘線程中調用同步執行 + 主隊列
// 使用 NSThread 的 detachNewThreadSelector 方法會建立線程,並自動啓動線程執行 selector 任務 [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
輸出結果以下:
2018-02-23 20:44:19.377321+0800 YSC-GCD-demo[20083:5040347] currentThread---{number = 3, name = (null)} 2018-04-23 20:44:19.377494+0800 YSC-GCD-demo[20083:5040347] syncMain---begin 2018-04-23 20:44:21.384716+0800 YSC-GCD-demo[20083:5040132] 1---{number = 1, name = main} 2018-04-23 20:44:23.386091+0800 YSC-GCD-demo[20083:5040132] 1---{number = 1, name = main} 2018-04-23 20:44:25.387687+0800 YSC-GCD-demo[20083:5040132] 2---{number = 1, name = main} 2018-4-23 20:44:27.388648+0800 YSC-GCD-demo[20083:5040132] 2---{number = 1, name = main} 2018-04-23 20:44:29.390459+0800 YSC-GCD-demo[20083:5040132] 3---{number = 1, name = main} 2018-04-23 20:44:31.391965+0800 YSC-GCD-demo[20083:5040132] 3---{number = 1, name = main} 2018-04-23 20:44:31.392513+0800 YSC-GCD-demo[20083:5040347] syncMain---end
在其餘線程中使用同步執行 + 主隊列可看到:
(1)全部任務都是在主線程(非當前線程)中執行的,沒有開啓新的線程(全部放在主隊列中的任務,都會放到主線程中執行)。
(2)全部任務都在打印的syncConcurrent---begin和syncConcurrent---end之間執行(同步任務須要等待隊列的任務執行結束)。
(3)任務是按順序執行的(主隊列是串行隊列,每次只有一個任務被執行,任務一個接一個按順序執行)。
爲何如今就不會卡住了呢?
由於syncMain 任務放到了其餘線程裏,而任務一、任務二、任務3都在追加到主隊列中,這三個任務都會在主線程中執行。syncMain 任務在其餘線程中執行到追加任務1到主隊列中,由於主隊列如今沒有正在執行的任務,因此,會直接執行主隊列的任務1,等任務1執行完畢,再接着執行任務二、任務3。因此這裏不會卡住線程。
4.6 異步執行 + 主隊列
只在主線程中執行任務,執行完一個任務,再執行下一個任務。
- (void)asyncMain { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 NSLog(@"asyncMain---begin"); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ // 追加任務1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_async(queue, ^{ // 追加任務2 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程 } }); dispatch_async(queue, ^{ // 追加任務3 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程 } }); NSLog(@"asyncMain---end"); }
執行結果以下:
2018-02-23 20:45:49.981505+0800 YSC-GCD-demo[20111:5046708] currentThread---{number = 1, name = main} 2018-04-23 20:45:49.981935+0800 YSC-GCD-demo[20111:5046708] asyncMain---begin 2018-04-23 20:45:49.982352+0800 YSC-GCD-demo[20111:5046708] asyncMain---end 2018-04-23 20:45:51.991096+0800 YSC-GCD-demo[20111:5046708] 1---{number = 1, name = main} 2018-04-23 20:45:53.991959+0800 YSC-GCD-demo[20111:5046708] 1---{number = 1, name = main} 2018-04-23 20:45:55.992937+0800 YSC-GCD-demo[20111:5046708] 2---{number = 1, name = main} 2018-04-23 20:45:57.993649+0800 YSC-GCD-demo[20111:5046708] 2---{number = 1, name = main} 2018-04-23 20:45:59.994928+0800 YSC-GCD-demo[20111:5046708] 3---{number = 1, name = main} 2018-04-23 20:46:01.995589+0800 YSC-GCD-demo[20111:5046708] 3---{number = 1, name = main}
在異步執行 + 主隊列能夠看到:
(1)全部任務都是在當前線程(主線程)中執行的,並無開啓新的線程(雖然異步執行具有開啓線程的能力,但由於是主隊列,因此全部任務都在主線程中)。
(2)全部任務是在打印的syncConcurrent---begin和syncConcurrent---end以後纔開始執行的(異步執行不會作任何等待,能夠繼續執行任務)。
(3)任務是按順序執行的(由於主隊列是串行隊列,每次只有一個任務被執行,任務一個接一個按順序執行)。
對於上面是GCD的基本介紹,下一篇將講述GCD的使用,謝謝觀看!!!