深入淺出iOS多線程(一)——線程的概念
深入淺出iOS多線程(二)——pthraed和NSThread的使用
深入淺出iOS多線程(三)——GCD多線程
深入淺出iOS多線程(四)——NSOperation多線程
深入淺出iOS多線程(五)——多線程鎖
GCD簡介
前面分析的pthread和NSThread多線程技術(shù),直接操作線程,而下面分析的GCD以及下一篇NSOpration都是并發(fā)解決技術(shù)
什么是GCD
- 全稱是Grand Central Dispatch,可譯為“宏大的中央調(diào)度”
- 純C語言,提供了非常強大的函數(shù)
- GCD完全面向過程的并發(fā)解決技術(shù)
GCD的優(yōu)勢
- GCD是蘋果公司為多核的并行運算提出的解決方案
- GCD會自動利用更多的CPU內(nèi)核(比如雙核、四核)
- GCD會自動管理多線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
GCD的兩個核心概念
- 任務(wù):執(zhí)行什么操作
- 隊列:用來存放任務(wù)
GCD的使用步驟
- 確定任務(wù)
- 將任務(wù)添加到隊列中
- GCD會自動將隊列的任務(wù)取出,放到對應(yīng)的線程中執(zhí)行
- 任務(wù)的取出遵循隊列的FIFO原則,先進先出,后進后出
- 程序員要做的,只是將任務(wù)添加到隊列,隊列按照程序員指定的方式,調(diào)度任務(wù)執(zhí)行任務(wù)的方法:同步和異步
- 同步:一個任務(wù)沒有結(jié)束,就不會執(zhí)行下一個任務(wù)
- 異步:不用等待任務(wù)執(zhí)行完畢,就會執(zhí)行下一個任務(wù)
GCD簡單使用
同步GCD
-
同步執(zhí)行方法,當前任務(wù)不執(zhí)行完成,就不會執(zhí)行下一個任務(wù),同步執(zhí)行不會開啟線程
//1.創(chuàng)建隊列 dispatch_queue_t g = dispatch_get_global_queue(0, 0); //2.將任務(wù)添加到隊列中 //2.1.定義任務(wù) void(^task)(void) = ^{ NSLog(@"%@",[NSThread currentThread]); }; //2.2 添加任務(wù)到隊列,并且直接執(zhí)行 dispatch_sync(g, task);
異步GCD
-
異步執(zhí)行方法,如果任務(wù)沒有執(zhí)行完畢,可以不用等待,異步執(zhí)行下一個任務(wù),具備開啟線程的能力,異步通常又是多線程的代名詞
//1.創(chuàng)建隊列 dispatch_queue_t g = dispatch_get_global_queue(0, 0); //2.將任務(wù)添加到隊列中 //2.1.定義任務(wù) void(^task)(void) = ^{ NSLog(@"%@",[NSThread currentThread]); }; //2.2 添加任務(wù)到隊列,并且直接執(zhí)行 dispatch_async(g, task);
GCD的線程通信(GCD主線程)
- 主隊列,更新UI,專門負責(zé)在主線程上調(diào)度任務(wù)的隊列
- 主線程中用同步跟異步都是一樣的
dispatch_async(dispatch_get_global_queue(0, 0 ), ^{
NSLog(@"%@",[NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
});
GCD子線程下載圖片,主線程更新UI
- 用GCD來異步下載圖片,主線程中更新UI
- 簡單邏輯來講,用GCD更加簡潔,看著就想一氣呵成很爽
- 開發(fā)是要看線程的通信(線程中的業(yè)務(wù)邏輯是否過于復(fù)雜),如果過于復(fù)雜,建議使用@selector()的形式去使用。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL * url = [NSURL URLWithString:@"https://images.unsplash.com/photo-1496840220025-4cbde0b9df65?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
[self.imageView sizeToFit];
self.scrollView.contentSize = image.size;
});
});
GCD隊列
先把GCD異步、同步和隊列、block的概念梳理一遍:
-
GCD 核心概念:將任務(wù)添加到隊列,指定任務(wù)執(zhí)行的方法
- 任務(wù)
- 使用block封裝
- block就是一個提前準備的代碼塊,在需要的時候執(zhí)行
- 隊列(負責(zé)調(diào)度任務(wù))
- 串行隊列:一個接一個的調(diào)度任務(wù)
- 并發(fā)隊列:可以同時調(diào)度多個任務(wù)
- 任務(wù)執(zhí)行函數(shù)(任務(wù)都需要在線程中執(zhí)行)
- 同步執(zhí)行:當前指令不完成,不會執(zhí)行下個指令
- 異步執(zhí)行:當前指令不完成,不會等待,同樣執(zhí)行下個指令
- 任務(wù)
GCD串行隊列
- 串行隊列,同步任務(wù)
- 不會開啟線程,順序執(zhí)行
- (void)gcdQueueDemo1{
//隊列:串行
/**
1.隊列名稱
2.隊列的屬性 :DISPATCH_QUEUE_SERIAL == NULL 標示串行
**/
dispatch_queue_t q = dispatch_queue_create("struggle3g", NULL);
//2.同步執(zhí)行任務(wù)
for (int i = 0; i<10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@,%d",[NSThread currentThread],i);
});
}
}
- 串行隊列,異步任務(wù)
- 不會開啟線程,順序執(zhí)行
- (void)gcdQueueDemo2{
//隊列:串行
/**
1.隊列名稱
2.隊列的屬性 :DISPATCH_QUEUE_SERIAL == NULL 標示串行
**/
dispatch_queue_t q = dispatch_queue_create("struggle3g", NULL);
//2.異步執(zhí)行任務(wù)
for (int i = 0; i<10; i++) {
dispatch_async(q, ^{
NSLog(@"%@,%d",[NSThread currentThread],i);
});
}
}
- 總結(jié):
- 會不會去線程池中拿線程是由任務(wù)類型來決定(異步or同步)
- 串行隊列:肯定是先進先出,
- 當一個任務(wù)在執(zhí)行的時候,下一個任務(wù)是沒有執(zhí)行的可能的
- 在串行隊列中,每次只能執(zhí)行一個任務(wù),而不管你任務(wù)是異步還是同步,隊列已經(jīng)把下一個任務(wù)給封住了,只有串行隊列中的任務(wù)完成了,才能執(zhí)行下一個任務(wù)。
并行隊列
-
并發(fā)隊列,異步任務(wù)
- 會開線程,不會順序執(zhí)行
- (void)gcdQueueDemo3{ //隊列:并發(fā) /** 1.隊列名稱 2.隊列的屬性 :DISPATCH_QUEUE_CONCURRENT 并發(fā)隊列 **/ dispatch_queue_t q = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT); //2.異步執(zhí)行任務(wù) for (int i = 0; i<10; i++) { dispatch_async(q, ^{ NSLog(@"%@,%d",[NSThread currentThread],i); }); } } -
并發(fā)隊列,同步任務(wù)
- 不會開線程,按順序執(zhí)行
- (void)gcdQueueDemo4{ //隊列:并發(fā) /** 1.隊列名稱 2.隊列的屬性 :DISPATCH_QUEUE_CONCURRENT 并發(fā)隊列 **/ dispatch_queue_t q = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT); //2.異步執(zhí)行任務(wù) for (int i = 0; i<10; i++) { dispatch_sync(q, ^{ NSLog(@"%@,%d",[NSThread currentThread],i); }); } }
全局隊列
全劇隊列創(chuàng)建API
dispatch_queue_global_t
dispatch_get_global_queue(long identifier, unsigned long flags);
參數(shù)介紹:涉及到系統(tǒng)適配
-
iOS 8 服務(wù)質(zhì)量
QOS_CLASS_USER_INTERACTIVE 用戶交互(希望線程快速被執(zhí)行,不要用好使的操作) QOS_CLASS_USER_INITIATED 用戶需要的(同樣不要使用耗時操作) QOS_CLASS_DEFAULT 默認的(給系統(tǒng)來重置隊列的) QOS_CLASS_UTILITY 使用工具(用來做耗時操作) QOS_CLASS_BACKGROUND 后臺 QOS_CLASS_UNSPECIFIED 沒有指定優(yōu)先級 -
iOS 7 調(diào)度的優(yōu)先級
- DISPATCH_QUEUE_PRIORITY_HIGH 2 高優(yōu)先級 - DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認優(yōu)先級 - DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優(yōu)先級 - DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺優(yōu)先級
提示:盡可能不要選擇BACKGROUND 優(yōu)先級,服務(wù)質(zhì)量,線程執(zhí)行會很慢
- 第一個參數(shù):identifier:iOS8給服務(wù)質(zhì)量,iOS7是優(yōu)先級
第二個參數(shù):flags:為未來使用的一個保留值,現(xiàn)在始終給0
隊列總結(jié)
開不開線程,取決于執(zhí)行任務(wù)的函數(shù),同步不開,異步才能開
開幾條線程,取決于隊列,串行開一條,并發(fā)可以開多條(異步)
-
全局隊列 & 并發(fā)隊列
- 1> 名稱,并發(fā)隊列取名字,適合于企業(yè)開發(fā)跟蹤錯誤
- 2> release,在MRC 并發(fā)隊列 需要使用的
- dispatch_release(q);//ARC 情況下不需要release !
-
全局隊列 & 串行隊列
- 全局隊列: 并發(fā),能夠調(diào)度多個線程,執(zhí)行效率高
- 費電 性能提高
串行隊列:一個一個執(zhí)行,執(zhí)行效率低 - 省電 性能低
-
判斷依據(jù):用戶上網(wǎng)方式
- WIFI : 可以多開線程
- 流量 : 盡量少開線程
GCD隊列實際應(yīng)用
在開發(fā)中,通常將耗時操作放入后臺執(zhí)行,有時候,有些任務(wù)彼此有依賴關(guān)系
-
例子:購買->登陸->支付,3種方式,代碼如下
//一. 并發(fā)隊列 同步任務(wù) dispatch_queue_t buyqueue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT); //1. 用戶購買 dispatch_sync(buyqueue, ^{ NSLog(@"用戶購買"); }); //2. 用戶登陸 dispatch_sync(buyqueue, ^{ NSLog(@"用戶登陸"); }); //3. 用戶支付 dispatch_sync(buyqueue, ^{ NSLog(@"用戶支付"); }); //二. 串行隊列 異步任務(wù) dispatch_queue_t buyqueue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_SERIAL); //1. 用戶購買 dispatch_async(buyqueue, ^{ NSLog(@"用戶購買"); }); //2. 用戶登陸 dispatch_async(buyqueue, ^{ NSLog(@"用戶登陸"); }); //3. 用戶支付 dispatch_async(buyqueue, ^{ NSLog(@"用戶支付"); }); //三. 串行隊列 同步任務(wù) dispatch_queue_t buyqueue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_SERIAL); //1. 用戶購買 dispatch_sync(buyqueue, ^{ NSLog(@"用戶購買"); }); //2. 用戶登陸 dispatch_sync(buyqueue, ^{ NSLog(@"用戶登陸"); }); //3. 用戶支付 dispatch_sync(buyqueue, ^{ NSLog(@"用戶支付"); });
例子:并發(fā)隊列調(diào)度多個任務(wù)前,指定一個同步任務(wù),讓所有的異步任務(wù),等待同步任務(wù)執(zhí)行完成,這就是依賴關(guān)系
-
同步任務(wù),會造成一個死鎖!
dispatch_queue_t q = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT); //任務(wù) void (^task)()=^{ for (int i = 0; i < 10; i++) { NSLog(@"%d %@",i ,[NSThread currentThread]); } //1.用戶登錄 dispatch_sync(q, ^{ NSLog(@"用戶登錄 %@",[NSThread currentThread]); }); //2.支付 dispatch_async(q, ^{ NSLog(@"支付 %@",[NSThread currentThread]); }); //3.下載 dispatch_async(q, ^{ NSLog(@"下載 %@",[NSThread currentThread]); }); }; dispatch_async(q, task);
GCD延遲執(zhí)行(GCD定時器)
- 從現(xiàn)在開始,過了多少納秒之后,讓 queue隊列,調(diào)度 block 任務(wù),異步執(zhí)行!
代碼如下:
/**
參數(shù):
1.dispatch_time_t
2.queue
3.block
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.00003 * NSEC_PER_SEC));
dispatch_after(when, dispatch_queue_create("struggle3g", NULL), ^{
NSLog(@"%@",[NSThread currentThread]);
});
GCD一次執(zhí)行
- 蘋果提供的 一次執(zhí)行機制,不僅能夠保證一次執(zhí)行!而且是線程安全的!!
- 更多的時候是用這種機制來做一個單例
//蘋果提供的 一次執(zhí)行機制,不僅能夠保證一次執(zhí)行!而且是線程安全的!!
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
//蘋果推薦使用 gcd 一次執(zhí)行,效率高
//不要使用互斥鎖,效率低!
dispatch_once(&onceToken, ^{
//只會執(zhí)行一次!!
NSLog(@"執(zhí)行了%@",[NSThread currentThread]);
});
GCD調(diào)度組(Group)
- 創(chuàng)建隊列
- 創(chuàng)建調(diào)度組
- 添加任務(wù),讓隊列調(diào)度
- 所有線程任務(wù)執(zhí)行完畢,通知
//1.隊列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2.調(diào)用組
dispatch_group_t g = dispatch_group_create();
//3.添加任務(wù),讓隊列調(diào)度,任務(wù)執(zhí)行情況,最后通知群組
dispatch_group_async(g, q, ^{
NSLog(@"downloadA %@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"downloadB %@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
NSLog(@"downloadC %@",[NSThread currentThread]);
});
//4.所有任務(wù)執(zhí)行完畢,通知
//dispatch_group_notify 本身也是異步的
dispatch_group_notify(g, q, ^{
NSLog(@"OK %@",[NSThread currentThread]);
});
//如果想要dispatch_group_notify直接在主線程需要在其中傳入主線程隊列
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
NSLog(@"OK %@",[NSThread currentThread]);
});
GCD主隊列
dispatch_queue_t mainqueue = dispatch_get_main_queue();
主隊列 & 串行隊列的區(qū)別
都是 一個一個安排任務(wù)
隊列特點:FIFO
并發(fā)隊列 可以調(diào)度很多任務(wù)
-
串行獨立, 必須等待一個任務(wù)執(zhí)行完成,再調(diào)度另外一個
- 最多只能開啟一條線程
-
主隊列,以FIFO調(diào)度任務(wù),如果主線程上有任務(wù)在執(zhí)行,主隊列就不會調(diào)度任務(wù)
- 主要是負責(zé)在主線程上執(zhí)行任務(wù)
主隊列同步任務(wù)
-
崩潰
- 主隊列無法實現(xiàn)同步任務(wù)
NSLog(@"這里!!%@",[NSThread currentThread]); //1.隊列 --> 已啟動主線程,就可以獲取主隊列 dispatch_queue_t q = dispatch_get_main_queue(); //2.同步任務(wù) dispatch_sync(q, ^{ NSLog(@"我要崩潰 %@",[NSThread currentThread]); }); NSLog(@"come here");
主線程異步任務(wù)
- 主隊列是專門負責(zé)在主線程上調(diào)度任務(wù)的隊列 --> 不會開線程
//1.隊列 --> 已啟動主線程,就可以獲取主隊列
dispatch_queue_t q = dispatch_get_main_queue();
//2.異步任務(wù)
dispatch_async(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");
主線程同步任務(wù)(不死鎖)
void (^task)(void) = ^{
NSLog(@"這里!!%@",[NSThread currentThread]);
//1.隊列 --> 已啟動主線程,就可以獲取主隊列
dispatch_queue_t q = dispatch_get_main_queue();
//2.同步任務(wù)
dispatch_sync(q, ^{
NSLog(@"主線程 %@",[NSThread currentThread]);
});
NSLog(@"come here");
};
//開啟一個線程
dispatch_async(dispatch_get_global_queue(0, 0), task);
GCD(Apply)重復(fù)調(diào)用
- 重復(fù)執(zhí)行某個任務(wù),但是注意這個方法沒有辦法異步執(zhí)行(為了不阻塞線程可以使用dispatch_async()包裝一下再執(zhí)行)。
全局并發(fā)隊列中的Apply
/*
全局并發(fā)隊列
結(jié)果驗證:并發(fā)隊列,開啟子線程,亂序執(zhí)行
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t count) {
NSLog(@"count = %zu,%@",count, [NSThread currentThread]);
});
/*
并發(fā)隊列
結(jié)果驗證:并發(fā)隊列,開啟子線程,亂序執(zhí)行
*/
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t count) {
NSLog(@"count = %zu,%@",count, [NSThread currentThread]);
});
/*
串行隊列
結(jié)果驗證:串行隊列,按順序執(zhí)行
*/
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_SERIAL);
dispatch_apply(10, queue, ^(size_t count) {
NSLog(@"count = %zu,%@",count, [NSThread currentThread]);
});
注意主線程隊列中的Apply
-
主線程中的Apply直接崩潰,在堆棧中我們可以看到的信息
__DISPATCH_WAIT_FOR_QUEUE__- 調(diào)度等待隊列
- 線程死鎖,由于在主線程開啟了一個同步任務(wù),而主線程運行到
dispatch_sync的時候,dispatch_sync中的內(nèi)容需要讓主線程其他任務(wù)等待它完成時才能運行,而主線程其他任務(wù)也是需要dispatch_sync等待它們執(zhí)行完成時才能調(diào)用,從而造成線程的死鎖問題。
//主線程隊列 同步任務(wù) dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ NSLog(@"%@", [NSThread currentThread]); });
GCD(dispatch_barrier_async())
- 使用此方法創(chuàng)建的任務(wù)首先會查看隊列中有沒有別的任務(wù)要執(zhí)行,如果有,則會等待已有任務(wù)執(zhí)行完畢再執(zhí)行;
- 同時在此方法后添加的任務(wù)必須等待此方法中任務(wù)執(zhí)行后才能執(zhí)行。
- 利用這個方法可以控制執(zhí)行順序,例如前面先加載最后一張圖片的需求就可以先使用這個方法將最后一張圖片加載的操作添加到隊列,然后調(diào)用dispatch_async()添加其他圖片加載任務(wù)
dispatch_barrier_async顧名思義就是柵欄函數(shù),就是等于在一個并發(fā)隊列中,在多個任務(wù)之間增加一道柵欄,當柵欄以及柵欄之前的任務(wù)完成以后還能執(zhí)行,柵欄以后的代碼。
```
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"++1++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"++2++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"++3++%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"柵欄函數(shù)");
});
dispatch_async(queue, ^{
NSLog(@"++4++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"++5++%@",[NSThread currentThread]);
});
```
輸出結(jié)果一定是 1、2、3前面三個部分順序執(zhí)行,第四個一定是柵欄函數(shù),后面2個順序打亂。
dispatch_barrier_async 和dispatch_barrier_sync的區(qū)別就是阻不阻當前線程
GCD信號量操作
信號量的API
dispatch_semaphore_t dispatch_semaphore_create(long value);
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
-
dispatch_semaphore_create創(chuàng)建一個dispatch_semaphore_t的信號量,并且創(chuàng)建的時候需要指定信號量的大小,dispatch_semaphore_wait等待信號量,如果信號量的值為0,那么該函數(shù)就會一直等待,也就是不返回(相當于阻塞當前線程),直到該函數(shù)
等待的信號量的值大于等于1,該函數(shù)會對信號量的值進行減1操作,然后返回
dispatch_semaphore_signal發(fā)送信號量,該函數(shù)會對信號量的值進行+1操作。
并發(fā)隊列,異步執(zhí)行實現(xiàn)同步操作
首先建立一個并發(fā)隊列,異步執(zhí)行的操作
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"++1++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"++2++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"++3++%@",[NSThread currentThread]);
});
運行結(jié)果
++3++<NSThread: 0x600003b7c840>{number = 4, name = (null)}
++2++<NSThread: 0x600003b6d640>{number = 5, name = (null)}
++1++<NSThread: 0x600003b77a80>{number = 3, name = (null)}
從上述結(jié)果來看,隊列中的任務(wù)并不是同步執(zhí)行的,那么我們現(xiàn)在就可以使用信號量在進行同步操作,代碼如下:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"++1++%@",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"++2++%@",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"++3++%@",[NSThread currentThread]);
});
打印結(jié)果如下:
++1++<NSThread: 0x6000029e73c0>{number = 3, name = (null)}
++2++<NSThread: 0x6000029ef280>{number = 4, name = (null)}
++3++<NSThread: 0x6000029ef280>{number = 4, name = (null)}
讓多個異步任務(wù)按照同步順序執(zhí)行,直接在同步隊列異步操作不是更好嗎?為什么要使用復(fù)雜的信號量,這是因為,同步隊列中當中只是在一個線程當中運行,而信號量可以充分利用多線程,它會開啟子線程。所以同步隊列異步操作喪失了并發(fā)執(zhí)行的可能性。雖然可以完成任務(wù),但是卻沒有充分發(fā)揮CPU多線程的優(yōu)勢
GCD結(jié)構(gòu)圖
