GCD簡介
GCD全稱是Grand Central Dispatch
純C語言幽歼,提供例如非常強(qiáng)大的函數(shù)
GCD優(yōu)勢(shì)
GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)
GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程凸丸、調(diào)度任務(wù)怎棱、銷毀線程)
程序員只需要告訴GCD想要執(zhí)行什么任務(wù)九默,不需要編寫任何線程管理代碼
【重點(diǎn)】用一句話總結(jié)GCD就是:將任務(wù)添加到隊(duì)列蒸殿,并指定任務(wù)執(zhí)行的函數(shù)
GCD核心
在日常開發(fā)中,GCD一般寫成下面這種形式:
dispatch_async( dispatch_queue_create("com.CJL.Queue", NULL), ^{
NSLog(@"GCD基本使用");
});
將上述代碼拆分恬总,方便我們來理解GCD的核心 主要是由 任務(wù) + 隊(duì)列 + 函數(shù) 構(gòu)成
//********GCD基礎(chǔ)寫法********
//創(chuàng)建任務(wù)
dispatch_block_t block = ^{
NSLog(@"hello GCD");
};
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", NULL);
//將任務(wù)添加到隊(duì)列前普,并指定函數(shù)執(zhí)行
dispatch_async(queue, block);
使用dispatch_block_t創(chuàng)建任務(wù)
使用dispatch_queue_t創(chuàng)建隊(duì)列
將任務(wù)添加到隊(duì)列,并指定執(zhí)行任務(wù)的函數(shù)dispatch_async
注意
這里的任務(wù)是指執(zhí)行操作的意思壹堰,在使用dispatch_block_t創(chuàng)建任務(wù)時(shí)拭卿,主要有以下兩點(diǎn)說明
任務(wù)使用block封裝
任務(wù)的block沒有參數(shù)也沒有返回值
函數(shù)與隊(duì)列
函數(shù)
在GCD中執(zhí)行任務(wù)的方式有兩種,同步執(zhí)行和異步執(zhí)行贱纠,分別對(duì)應(yīng) 同步函數(shù)dispatch_sync 和 異步函數(shù)dispatch_async,兩者對(duì)比如下
同步執(zhí)行峻厚,對(duì)應(yīng)同步函數(shù)dispatch_sync
必須等待當(dāng)前語句執(zhí)行完畢,才會(huì)執(zhí)行下一條語句
不會(huì)開啟線程谆焊,即不具備開啟新線程的能力
在當(dāng)前線程中執(zhí)行block任務(wù)
異步執(zhí)行惠桃,對(duì)應(yīng)異步函數(shù)dispatch_async
不用等待當(dāng)前語句執(zhí)行完畢,就可以執(zhí)行下一條語句
會(huì)開啟線程執(zhí)行block任務(wù),即具備開啟新線程的能力(但并不一定開啟新線程辜王,這個(gè)與任務(wù)所指定的隊(duì)列類型有關(guān))
異步 是 多線程 的代名詞
所以劈狐,綜上所述,兩種執(zhí)行方式的主要區(qū)別有兩點(diǎn):
是否等待隊(duì)列的任務(wù)執(zhí)行完畢
是否具備開啟新線程的能力
隊(duì)列
串行隊(duì)列 和 并發(fā)隊(duì)列
多線程中所說的隊(duì)列(Dispatch Queue)是指執(zhí)行任務(wù)的等待隊(duì)列誓禁,即用來存放任務(wù)的隊(duì)列懈息。隊(duì)列是一種特殊的線性表肾档,遵循先進(jìn)先出(FIFO)原則摹恰,即新任務(wù)總是被插入到隊(duì)尾,而任務(wù)的讀取從隊(duì)首開始讀取怒见。每讀取一個(gè)任務(wù)俗慈,則從隊(duì)列中釋放一個(gè)任務(wù),如下圖所示:
在GCD中遣耍,隊(duì)列主要分為串行隊(duì)列(Serial Dispatch Queue) 和并發(fā)隊(duì)列(Concurrent Dispatch Queue)兩種闺阱,如下圖所示:
串行隊(duì)列:每次只有一個(gè)任務(wù)被執(zhí)行,等待上一個(gè)任務(wù)執(zhí)行完畢再執(zhí)行下一個(gè)舵变,即只開啟一個(gè)線程(通俗理解:同一時(shí)刻只調(diào)度一個(gè)任務(wù)執(zhí)行)
使用dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);創(chuàng)建串行隊(duì)列
其中的DISPATCH_QUEUE_SERIAL也可以使用NULL表示酣溃,這兩種均表示 默認(rèn)的串行隊(duì)列
// 串行隊(duì)列的獲取方法
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.CJL.Queue", NULL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_SERIAL);
并發(fā)隊(duì)列:一次可以并發(fā)執(zhí)行多個(gè)任務(wù),即開啟多個(gè)線程纪隙,并同時(shí)執(zhí)行任務(wù)(通俗理解:同一時(shí)刻可以調(diào)度多個(gè)任務(wù)執(zhí)行)
使用dispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);創(chuàng)建并發(fā)隊(duì)列
注意:并發(fā)隊(duì)列的并發(fā)功能只有在異步函數(shù)下才有效
// 并發(fā)隊(duì)列的獲取方法
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);
主隊(duì)列 和 全局并發(fā)隊(duì)列
在GCD中赊豌,針對(duì)這兩種隊(duì)列,分別提供了主隊(duì)列(Main Dispatch Queue)和全局并發(fā)隊(duì)列(Global Dispatch Queue)
主隊(duì)列(Main Dispatch Queue):GCD中提供的特殊的串行隊(duì)列
專門用來在主線程上調(diào)度任務(wù)的串行隊(duì)列绵咱,依賴于主線程碘饼、主Runloop,在main函數(shù)調(diào)用之前自動(dòng)創(chuàng)建
不會(huì)開啟線程
如果當(dāng)前主線程正在有任務(wù)執(zhí)行悲伶,那么無論主隊(duì)列中當(dāng)前被添加了什么任務(wù)艾恼,都不會(huì)被調(diào)度
使用dispatch_get_main_queue()獲得主隊(duì)列
通常在返回主線程 更新UI時(shí)使用
//主隊(duì)列的獲取方法
dispatch_queue_t mainQueue = dispatch_get_main_queue();
全局并發(fā)隊(duì)列(Global Dispatch Queue):GCD提供的默認(rèn)的并發(fā)隊(duì)列
為了方便程序員的使用,蘋果提供了全局隊(duì)列
在使用多線程開發(fā)時(shí)麸锉,如果對(duì)隊(duì)列沒有特殊需求钠绍,在執(zhí)行異步任務(wù)時(shí),可以直接使用全局隊(duì)列
使用dispatch_get_global_queue獲取全局并發(fā)隊(duì)列花沉,最簡單的是dispatch_get_global_queue(0, 0)
第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級(jí)柳爽,默認(rèn)優(yōu)先級(jí)為DISPATCH_QUEUE_PRIORITY_DEFAULT=0,在ios9之后主穗,已經(jīng)被服務(wù)質(zhì)量(quality-of-service)取代
第二個(gè)參數(shù)使用0
//全局并發(fā)隊(duì)列的獲取方法
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
//優(yōu)先級(jí)從高到低(對(duì)應(yīng)的服務(wù)質(zhì)量)依次為
- DISPATCH_QUEUE_PRIORITY_HIGH -- QOS_CLASS_USER_INITIATED
- DISPATCH_QUEUE_PRIORITY_DEFAULT -- QOS_CLASS_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW -- QOS_CLASS_UTILITY
- DISPATCH_QUEUE_PRIORITY_BACKGROUND -- QOS_CLASS_BACKGROUND
全局并發(fā)隊(duì)列 + 主隊(duì)列 配合使用
在日常開發(fā)中泻拦,全局隊(duì)列+并發(fā)并列一般是這樣配合使用的
//主隊(duì)列 + 全局并發(fā)隊(duì)列的日常使用
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//執(zhí)行耗時(shí)操作
dispatch_async(dispatch_get_main_queue(), ^{
//回到主線程進(jìn)行UI操作
});
});
函數(shù)與隊(duì)列的不同組合
串行隊(duì)列 + 同步函數(shù)
任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的在當(dāng)前線程執(zhí)行,不會(huì)開辟新線程
串行隊(duì)列 + 異步函數(shù)
【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行忽媒,會(huì)開辟新線程
并發(fā)隊(duì)列 + 同步函數(shù)
【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行争拐,不開辟線程
并發(fā)隊(duì)列 + 異步函數(shù)
【任務(wù)亂序執(zhí)行】:任務(wù)執(zhí)行無順序,會(huì)開辟新線程
主隊(duì)列 + 同步函數(shù)
【造成死鎖】:任務(wù)相互等待,造成死鎖
造成死鎖的原因分析如下:
主隊(duì)列有兩個(gè)任務(wù)架曹,順序?yàn)椋篘SLog任務(wù) - 同步block
執(zhí)行NSLog任務(wù)后隘冲,執(zhí)行同步Block,會(huì)將任務(wù)1(即i=1時(shí))加入到主隊(duì)列绑雄,主隊(duì)列順序?yàn)椋篘SLog任務(wù) - 同步block - 任務(wù)1
任務(wù)1的執(zhí)行需要等待同步block執(zhí)行完畢才會(huì)執(zhí)行展辞,而同步block的執(zhí)行需要等待任務(wù)1執(zhí)行完畢,所以就造成了任務(wù)互相等待的情況万牺,即造成死鎖崩潰
死鎖現(xiàn)象
主線程因?yàn)槟阃胶瘮?shù)的原因等著先執(zhí)行任務(wù)
主隊(duì)列等著主線程的任務(wù)執(zhí)行完畢再執(zhí)行自己的任務(wù)
主隊(duì)列和主線程相互等待會(huì)造成死鎖
主隊(duì)列 + 異步函數(shù)
【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行罗珍,不開辟線程
全局并發(fā)隊(duì)列 + 同步函數(shù)
【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行,不開辟新線程
全局并發(fā)隊(duì)列 + 異步函數(shù)
【任務(wù)亂序執(zhí)行】:任務(wù)亂序執(zhí)行脚粟,會(huì)開辟新線程