一、GCD簡(jiǎn)介
-
什么是GCD?
全稱是 Grand Central Dispatch
純 C 語(yǔ)言,提供了非常多強(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ù),不需要編寫任何線程管理代碼
-
劣勢(shì)
自動(dòng)控制生命周期,程序員不能自由操作
-
函數(shù)
- (void)syncTest { // 把任務(wù)添加到隊(duì)列 --> 函數(shù) // 任務(wù) ( _t ref 都是 c對(duì)象) dispatch_block_t block = ^{ NSLog(@"hello GCD"); }; //串行隊(duì)列 <- 因?yàn)榈诙€(gè)參數(shù)是NULL dispatch_queue_t queue = dispatch_queue_create("com.hehe.cn", NULL); // 函數(shù) dispatch_async(queue, block); }
- 任務(wù)使用 block 封裝
- 任務(wù)的 block 沒(méi)有參數(shù)也沒(méi)有返回值
- 執(zhí)行任務(wù)的函數(shù)
- 異步
dispatch_async
- 不用等待當(dāng)前語(yǔ)句執(zhí)行完畢,就可以執(zhí)行下一條語(yǔ)句
- 會(huì)開啟線程執(zhí)行 block的任務(wù)
- 異步是多線程的代名詞
- 同步
dispatch_sync
- 必須等待當(dāng)前語(yǔ)句執(zhí)行完畢,才會(huì)執(zhí)行下一條語(yǔ)句
- 不會(huì)開啟線程
- 在當(dāng)前執(zhí)行 block的任務(wù)
- 異步
-
隊(duì)列
串行隊(duì)列 - DISPATCH_QUEUE_SERIAL: 只能一個(gè)一個(gè)任務(wù)挨個(gè)出去
并行隊(duì)列 - DISPATCH_QUEUE_CONCURRENT: 多個(gè)任務(wù)同時(shí)出去
-
隊(duì)列和函數(shù)的組合
-
同步函數(shù)串行隊(duì)列:
1、不會(huì)開啟線程,在當(dāng)前線程執(zhí)行任務(wù)
2、任務(wù)串行執(zhí)行,任務(wù)一個(gè)接著一個(gè)
3、會(huì)產(chǎn)生堵塞
-
同步函數(shù)并發(fā)隊(duì)列:
1、不會(huì)開啟線程,在當(dāng)前線程執(zhí)行任務(wù)
2、任務(wù)一個(gè)接著一個(gè)
-
異步函數(shù)串行隊(duì)列:
1、開啟線程一條新線程
2、任務(wù)一個(gè)接著一個(gè)
-
異步函數(shù)并發(fā)隊(duì)列:
1、開啟線程,在當(dāng)前線程執(zhí)行任務(wù)
2、任務(wù)異步執(zhí)行,沒(méi)有順序,CPU調(diào)度有關(guān)
- (void)testDemo { dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_CONCURRENT); NSLog(@"1"); // 耗時(shí) dispatch_async(queue, ^{ NSLog(@"2"); dispatch_async(queue, ^{ NSLog(@"3"); }); NSLog(@"4"); }); NSLog(@"5"); // 打印: 1 5 2 4 3 // 因?yàn)閐ispatch_async不堵塞外部線程 // 假設(shè):一個(gè)通道代表串行,多個(gè)通道代表并發(fā),一輛車代表一個(gè)線程,每個(gè)通道有每輛車的出貨的順序 // 個(gè)人理解: // 車a:拉1,拉異步代碼塊:“我有車你先走”,拉5,其中異步代碼塊產(chǎn)生車b // 車b:拉2,拉異步代碼塊:“我有車你先走”,拉4,其中異步代碼塊產(chǎn)生車c // 車c:拉3 // // // }
- (void)testDemo2 { // 串行隊(duì)列 dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_SERIAL); NSLog(@"1"); // 異步函數(shù) dispatch_async(queue, ^{ NSLog(@"2"); // 同步 dispatch_sync(queue, ^{ //NSLog(@"3"); }); // 這個(gè)地方?jīng)]有代碼一樣會(huì)產(chǎn)生死鎖 //NSLog(@"4"); }); NSLog(@"5"); // 152 // 個(gè)人理解: // 串行隊(duì)列說(shuō)明只有一條通道,異步函數(shù)會(huì)創(chuàng)建多輛車,通道有每輛車的出貨的順序 // 以下是計(jì)劃: // 1、車a拉1,車a拉異步代碼塊,異步代碼塊說(shuō):“你先走,我自己有車”,車a拉了5走了 // 2、異步代碼塊中創(chuàng)建車b // 3、車b拉2,車b拉同步代碼塊,車b拉4 // 實(shí)際情況: // 車b在拉同步代碼塊的時(shí)候被告知:“我沒(méi)車,你得拉3,不然你啥也別干了” // 車b:“通道要求我得先把4拉了” // 于是: // 產(chǎn)生死鎖,3和4誰(shuí)也不拉 }
- (void)testDemo1 { dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_CONCURRENT); NSLog(@"1"); dispatch_async(queue, ^{ NSLog(@"2"); dispatch_sync(queue, ^{ NSLog(@"3"); }); NSLog(@"4"); }); NSLog(@"5"); //打印 :1 5 2 3 4 // 個(gè)人理解: // 并行隊(duì)列說(shuō)明有多條通道,同步函數(shù)會(huì)創(chuàng)建一輛車,每個(gè)通道有出貨的順序 // 以下是計(jì)劃: // 1、車a拉1,車a拉異步代碼塊,異步代碼塊說(shuō):“你先走,我自己有車”,車a拉了5走了 // 2、異步代碼塊中創(chuàng)建車b // 3、車b拉2,車b拉同步代碼塊,車b拉4 // 4、同步代碼塊說(shuō):“你得先拉3,不然你啥也別干了” // 5、車b說(shuō):“當(dāng)前通道要求我先拉4,我換個(gè)通道把3拉了再回來(lái)拉4” }
-
-
死鎖
- 主線程因?yàn)槟阃胶瘮?shù)的原因等著先執(zhí)行任務(wù)
- 主隊(duì)列等著主線程的任務(wù)執(zhí)行完畢再執(zhí)行自己的任務(wù)
- 主隊(duì)列和主線程相互等待會(huì)造成死鎖
-
特殊隊(duì)列
系統(tǒng)加載時(shí)候的默認(rèn)隊(duì)列:
-
dispatch_get_global_queue(0, 0);
全局隊(duì)列:并發(fā)隊(duì)列
不建議用
- 為了方便程序員的使用,蘋果提供了全局隊(duì)列
- 在使用多線程開發(fā)時(shí),如果對(duì)隊(duì)列沒(méi)有特殊需求,在執(zhí)行異步任務(wù)時(shí),可以直接使用全局隊(duì)列
-
dispatch_get_main_queue();
主隊(duì)列:串行隊(duì)列
- 專門用來(lái)在主線程上調(diào)度任務(wù)的隊(duì)列
- 不會(huì)開啟線程
- 如果當(dāng)前主線程正在有任務(wù)執(zhí)行,那么無(wú)論主隊(duì)列中當(dāng)前被添加了什么任務(wù),都不會(huì)被調(diào)度
int a = 0; while (a<10) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ a++; }); } //問(wèn)題1: // a會(huì)報(bào)錯(cuò),為什么? // //問(wèn)題2: // a會(huì)輸出什么值? // >=10 // 原因:資源搶奪 // 創(chuàng)建很多條線程,都會(huì)操作a //問(wèn)題3: // 輸出a最后的值該怎么修改? // __block int a = 0; //問(wèn)題1修復(fù) //把a(bǔ)從棧區(qū)copy成一個(gè)struct(中有a的指針地址和a的值) //block中會(huì)修改newA while (a<10) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ a++; }); } //問(wèn)題3 //最后 // 主線程 -- 堵塞 -- I/O -- 代碼執(zhí)行的耗時(shí) -- 相當(dāng)于一個(gè)同步效果 // 堵塞 -- 給你足夠的時(shí)間 -- 把前面的任務(wù)執(zhí)行完畢之后 -- 往隊(duì)列加入任務(wù) -- 再執(zhí)行 NSLog(@"主線程%d",a);//此時(shí)打印的也不是真的值 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"最后%d",a); });
-
二、GCD的應(yīng)用
-
柵欄函數(shù)
-
一定要是自定義的并發(fā)隊(duì)列,不然沒(méi)有效果
如果柵欄函數(shù)堵塞了全局并發(fā)隊(duì)列那系統(tǒng)就GG了
-
dispatch_barrier_async和dispatch_barrier_sync
- dispatch_barrier_async 前面的任務(wù)執(zhí)行完畢才會(huì)來(lái)到這里,不影響隊(duì)列外任務(wù)的執(zhí)行
- dispatch_barrier_sync 作用相同,但是這個(gè)會(huì)堵塞線程,影響后面的任務(wù)執(zhí)行,包括隊(duì)列外的任務(wù)
柵欄函數(shù)只能控制同一并發(fā)隊(duì)列 --- 不夠優(yōu)秀的地方 --- 不利于封裝
-
-
調(diào)度組
控制任務(wù)執(zhí)行順序
-
dispatch_group_create 創(chuàng)建組
dispatch_group_async 進(jìn)組任務(wù)
dispatch_group_notify 進(jìn)組任務(wù)執(zhí)行完畢通知
dispatch_group_wait 進(jìn)組任務(wù)執(zhí)行等待時(shí)間
-
dispatch_group_enter 進(jìn)組
dispatch_group_leave 出組
注意搭配使用
-
信號(hào)量dispatch_semaphore_t
Dispatch_Source