GCD详解
注意:本文對GCD進(jìn)行剖析的順序是:? GCD的概念--> API基本使用-->GCD源碼解讀--->常用場景的一些擴(kuò)展與對比(js的線程對比)
?
目錄
?
一、簡介
1、什么是GCD?看看百度百科的定義:?
2、使用GCD有哪些好處呢?
二、GCD任務(wù)和隊(duì)列?
1、什么是任務(wù)?
2、那么什么是同步執(zhí)行和異步執(zhí)行呢?
1、同步執(zhí)行(sync):?
2、異步執(zhí)行(async):?
3、什么是隊(duì)列(Dispatch Queue)?
三、GCD的使用步驟
1、隊(duì)列的創(chuàng)建方法/獲取方法
2、任務(wù)的創(chuàng)建方法?
3、那么這幾種不同組合方式各有什么區(qū)別呢?
四、基本使用
1、同步執(zhí)行+并發(fā)隊(duì)列?
2、異步執(zhí)行+并發(fā)隊(duì)列
3、同步執(zhí)行+串行隊(duì)列
4、異步執(zhí)行+串行隊(duì)列
5、主隊(duì)列
6、同步執(zhí)行+主隊(duì)列
1 、在主線程中調(diào)用同步執(zhí)行+主隊(duì)列
2、在其他線程中調(diào)用同步執(zhí)行+主隊(duì)列
7、異步執(zhí)行+主隊(duì)列
五、GCD線程間的通信
六、GCD其他API
1、異步柵欄+并發(fā)隊(duì)列:dispatch_barrier_async
2、同步柵欄+并發(fā)隊(duì)列:dispatch_barrier_sync
3、GCD的延遲執(zhí)行方法:dispatch_after
?1、在指定時(shí)間之后執(zhí)行某個(gè)任務(wù)
2、dispatch_after執(zhí)行時(shí)間不準(zhǔn)確證明:
4、GCD一次性代碼:dispatch_once
5、GCD快速迭代方法:dispatch_apply
6、GCD隊(duì)列組:dispatch_group
1、dispatch_group_notify
2、dispatch_group_wait
3、dispatch_group_enter、dispatch_group_leave
7、GCD信號量:
1、Dispatch Semaphore線程同步
2、Dispatch Semaphore線程安全和線程同步(為線程加鎖)
七、GCD源碼解讀之基于UNIX的操作系統(tǒng)內(nèi)核
1、Darwin
2、Mach
3、BSD
八、GCD源碼解讀之mach-o
1、mach-o文件類型分為(總共有11種):
1、MH_OBJECT (0x1 )
2、MH_EXECUTE (0x2) 可執(zhí)行文件
3、MH_DYLIB 動態(tài)庫文件
4、MH_DYLINKER (0x7) 動態(tài)鏈接編輯器
5、MH_DYSM 符號文件
2、Mach-O格式
3、執(zhí)行流程:
九、GCD源碼解讀之mach
十、源碼中常見的宏
1、__builtin_expect
2、 likely和unlikely
?3、?fastpath和slowpath
4、 os_atomic_cmpxchg
5、os_atomic_store2o
6、os_atomic_inc_orig
十一、常用數(shù)據(jù)結(jié)構(gòu)體
1、dispatch_queue_t
2、dispatch_continuation_t
3、dispatch_object_t
4、dispatch_function_t
十二、GCD相關(guān)的源碼
1、dispatch_queue_create
先從創(chuàng)建隊(duì)列開始。我們已經(jīng)很熟悉,創(chuàng)建隊(duì)列的方法是調(diào)用dispatch_queue_create函數(shù)。
1、_dispatch_queue_create_with_target
2、_dispatch_get_root_queue函數(shù)
3、_dispatch_root_queues數(shù)組
5、隊(duì)列的創(chuàng)建過程大致可以分為以下幾點(diǎn)
2、dispatch_sync
1、串行隊(duì)列
2、_dispatch_global_queue_poke函數(shù)
3、加入隊(duì)列代碼:
4、_dispatch_continuation_pop
5、_dispatch_root_queues_init函數(shù)
6、_pthread_workqueue_init_with_kevent
7、_dispatch_worker_thread3
3、dispatch_group_async
一、簡介
1、什么是GCD?看看百度百科的定義:?
GCD為Grand Central Dispatch的縮寫。Grand Central Dispatch (GCD)是Apple開發(fā)的一個(gè)多核編程的較新的解決方法。
全稱:
它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。
設(shè)計(jì):
GCD是一個(gè)替代諸如NSThread等技術(shù)的很高效和強(qiáng)大的技術(shù)。GCD完全可以處理諸如數(shù)據(jù)鎖定和資源泄漏等復(fù)雜的異步編程問題。GCD的工作原理是讓一個(gè)程序,根據(jù)可用的處理資源,安排他們在任何可用的處理器核心上平行排隊(duì)執(zhí)行特定的任務(wù)。這個(gè)任務(wù)可以是一個(gè)功能或者一個(gè)程序段。
GCD仍然在一個(gè)很低的水平使用線程,但是它不需要程序員關(guān)注太多的細(xì)節(jié)。GCD創(chuàng)建的隊(duì)列是輕量級的,蘋果聲明一個(gè)GCD的工作單元需要由15個(gè)指令組成。也就是說創(chuàng)造一個(gè)傳統(tǒng)的線程很容易的就會需要幾百條指令。
GCD中的一個(gè)任務(wù)可被用于創(chuàng)造一個(gè)被放置于隊(duì)列的工作項(xiàng)目或者事件源。如果一個(gè)任務(wù)被分配到一個(gè)事件源,那么一個(gè)由功能或者程序塊組成的工作單元會被放置于一個(gè)適當(dāng)?shù)年?duì)列中。蘋果公司認(rèn)為GCD相比于普通的一個(gè)接一個(gè)的執(zhí)行任務(wù)的方式更為有效率。
功能:
這個(gè)調(diào)度框架聲明了幾種數(shù)據(jù)類型和函數(shù)來創(chuàng)建和操作他們:
一、調(diào)度隊(duì)列
所有的調(diào)度隊(duì)列都是先進(jìn)先出隊(duì)列,因此,隊(duì)列中的任務(wù)的開始的順序和添加到隊(duì)列中的順序相同。GCD自動的為我們提供了一些調(diào)度隊(duì)列,我們也可以創(chuàng)建新的用于具體的目的。
下面列出幾種可用的調(diào)度隊(duì)列類型以及如何使用。
二、調(diào)度資源
它是一個(gè)監(jiān)視某些類型事件的對象。當(dāng)這些事件發(fā)生時(shí),它自動將一個(gè)block放入一個(gè)調(diào)度隊(duì)列的執(zhí)行例程中。
三、調(diào)度組
允許將多任務(wù)分組來方便后來加入執(zhí)行。任務(wù)能作為一個(gè)組中的一個(gè)成員被加到隊(duì)列中,客戶端能用這個(gè)組對象來等待直到這個(gè)組中的所有任務(wù)完成。
四、調(diào)度信號量
允許客戶端并行發(fā)布一定數(shù)量的任務(wù)。
2、使用GCD有哪些好處呢?
- GCD 可用于多核的并行運(yùn)算?
- GCD 會自動利用更多的 CPU 內(nèi)核 (比如雙核、四核)?
- GCD 會自動管理線程的生命周期(創(chuàng)建線程、請度任務(wù)、銷段線程)?
- 程序員只需要告訴 GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼?
二、GCD任務(wù)和隊(duì)列?
1、什么是任務(wù)?
- ?任務(wù)就是執(zhí)行操作的患思,換句話說就是你在線程中執(zhí)行的那段代碼。在GCD中是放在 block 中的。
- 執(zhí)行任務(wù)有兩種方式:同步執(zhí)行(sync)和異步執(zhí)行(async)。
- 兩者的主要區(qū)別是:是否等待隊(duì)列的任務(wù)執(zhí)行結(jié)束,以及是否具備開啟新線程的能力。?
2、那么什么是同步執(zhí)行和異步執(zhí)行呢?
1、同步執(zhí)行(sync):?
- 同步添加任務(wù)到指定的隊(duì)列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會一直等待,直到隊(duì)列里面的任務(wù)完成 之后再繼續(xù)執(zhí)行。?
- 只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力。?
2、異步執(zhí)行(async):?
- 異步添加任務(wù)到指定的隊(duì)列中,它不會健任何等待,可以維續(xù)執(zhí)行任務(wù)。?
- 可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力。
舉個(gè)例子:
老張愛喝茶,廢話不說,煮開水。
出場人物:老張,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。
1?老張把水壺放到火上,立等水開。(同步阻塞)
老張覺得自己有點(diǎn)傻
2?老張把水壺放到火上,去客廳看電視,時(shí)不時(shí)去廚房看看水開沒有。(同步非阻塞)
老張還是覺得自己有點(diǎn)傻,于是變高端了,買了把會響笛的那種水壺。水開之后,能大聲發(fā)出嘀~~~~的噪音。
3?老張把響水壺放到火上,立等水開。(異步阻塞)
老張覺得這樣傻等意義不大
4?老張把響水壺放到火上,去客廳看電視,水壺響之前不再去看它了,響了再去拿壺。(異步非阻塞)
老張覺得自己聰明了。
所謂同步異步,只是對于水壺而言。普通水壺,同步;響水壺,異步。雖然都能干活,但響水壺可以在自己完工之后,提示老張水開了。這是普通水壺所不能及的。同步只能讓調(diào)用者去輪詢自己(情況2中),造成老張效率的低下。
所謂阻塞非阻塞,僅僅對于老張而言。立等的老張,阻塞;看電視的老張,非阻塞。情況1和情況3中老張就是阻塞的,媳婦喊他都不知道。雖然3中響水壺是異步的,可對于立等的老張沒有太大的意義。所以一般異步是配合非阻塞使用的,這樣才能發(fā)揮異步的效用。
注意:異步執(zhí)行雖然具有開啟新線程的能力,但并不一定開啟新線程,這個(gè)和任務(wù)所指定的隊(duì)列有關(guān)
3、什么是隊(duì)列(Dispatch Queue)?
隊(duì)列是 一種特排的線性表,采用FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)。隊(duì)列的結(jié)構(gòu)可參考下圖
在GCD中有兩種隊(duì)列:串行隊(duì)列和并發(fā)隊(duì)列。兩者都符合FIFO(先進(jìn)先出)的原則。兩者的主要區(qū)別是:執(zhí)行順序不同,以及開啟線程數(shù)不同。
- 串行隊(duì)列(Serial Dispatch Queue):?每次只有一個(gè)任務(wù)被執(zhí)行。讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行。(只開啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完后, 再執(zhí)行下一個(gè)任務(wù))?
- 并發(fā)以列(Concurrent Dispatch Queue) :可以讓多個(gè)任務(wù)并發(fā)執(zhí)行。(可以開啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))?
- 注意:并發(fā)隊(duì)列的并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效?
?
三、GCD的使用步驟
GCD的使用只有兩步:
- 創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列或并發(fā)隊(duì)列)?
- 特任務(wù)追加到任務(wù)的等特隊(duì)列中,然后系統(tǒng)就會根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)
1、隊(duì)列的創(chuàng)建方法/獲取方法
可以使用dispatch.queue_create來創(chuàng)建隊(duì)列,需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識符,用于DEBUG,可為空,Dispatch Queue的名稱推薦使用應(yīng)用程序 ID 這種逆序全稱域名:第二個(gè)參數(shù)用來識別是串行隊(duì)列還是并發(fā)以列,DISPATCH_QUEUE_SERIAL表示串行以列,DISPATCH_QUEUE_CONCURRENT 表示并發(fā)隊(duì)列。?
// 串行隊(duì)列的包建方法dispatch_queue_t queue1 = dispatch_queue_create("com.lz.testQueueSerial",DISPATCH_QUEUE_SERIAL);//并發(fā)以列的創(chuàng)建方法dispatch_queue_t queue2 = dispatch_queue_create("com.lz.testQueueConcurrent",DISPATCH_QUEUE_CONCURRENT);對于串行隊(duì)列,GCD提供了的一種特殊的串行隊(duì)列:主隊(duì)列(Main Dispatch Queue)。?
- 所有放在主隊(duì)列中的任務(wù),都會放到主線程中執(zhí)行。?
- 可使用dispatch_get_main_queue()獲得主隊(duì)列。?
對于并發(fā)隊(duì)列,GCD 默認(rèn)提供了全局并發(fā)隊(duì)列(Global Dispatch Queue)。?
可以使用dispatch_get_global_queue來獲取。需要傳入再個(gè)參數(shù)。第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT,第二個(gè)參數(shù)暫時(shí)沒用,用0即可。?
2、任務(wù)的創(chuàng)建方法?
GCD提供了同步執(zhí)行任務(wù)的創(chuàng)建方法dispatch_sync和異步執(zhí)行任務(wù)創(chuàng)建方法dispatch_async。
//同步執(zhí)行任務(wù)創(chuàng)建方法dispatch_sync(queue1, ^{//這里放同步執(zhí)行任務(wù)代碼});//異步執(zhí)行任務(wù)創(chuàng)建方法dispatch_async(queue2, ^{//這里放異步執(zhí)行任務(wù)代碼});雖然使用GCD只需兩步,但是既然我們有兩種隊(duì)列(串行隊(duì)列/并發(fā)隊(duì)列),兩種任務(wù)執(zhí)行方式 (同步執(zhí)行/異步執(zhí)行),那么我們就有了四種不同的組合方式。再加上兩種特殊的隊(duì)列(全局并發(fā)隊(duì)列、主隊(duì)列),全局并發(fā)隊(duì)列可以作為普通并發(fā)隊(duì) 列來使用。但是主隊(duì)列因?yàn)橛悬c(diǎn)特殊,所以我們就又多了兩種組合方式。所以一共有六種不同的組合方式:?
- 同步執(zhí)行+并發(fā)隊(duì)列?
- 異步執(zhí)行 + 并發(fā)隊(duì)列?
- 同步執(zhí)行+串行隊(duì)列?
- 異步執(zhí)行 + 串行隊(duì)列?
- 同步執(zhí)行+主隊(duì)列?
- 異步執(zhí)行+主隊(duì)列?
?
3、那么這幾種不同組合方式各有什么區(qū)別呢?
| 區(qū)別 | 并發(fā)隊(duì)列 | 串行隊(duì)列 | 主隊(duì)列 |
| 同步(sync) | 沒有開啟新線程 | 串行執(zhí)行任務(wù)沒有開啟新線程 | 串行執(zhí)行任務(wù) 主線程調(diào)用:死鎖 卡住不執(zhí)行? 其他線程調(diào)用:沒有開啟新線程,串行執(zhí)行任務(wù) |
| 異步(async) | 有開啟新線程,并發(fā)執(zhí)行任務(wù)有開啟新線程(1條) | 串行執(zhí)行任務(wù)沒有開啟新線程 | 串行執(zhí)行任務(wù) |
?
?
?
?
?
四、基本使用
此部分按照先并發(fā)后串行的方式介紹
1、同步執(zhí)行+并發(fā)隊(duì)列?
在當(dāng)前線程中執(zhí)行任務(wù),不會開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。
//同步執(zhí)行+并發(fā)隊(duì)列 //特點(diǎn):在當(dāng)前線程中執(zhí)行任務(wù),不會開啟新線程,執(zhí)行完-一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。 - (void)syncConcurrent{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"syncConcurrent---begin");dispatch_queue_t queue = dispatch_queue_create("com.lz.testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_sync(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_sync(queue, ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_sync(queue, ^{//追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"syncConcurrent---end");}?打印結(jié)果:
?
結(jié)果分析(同步執(zhí)行+并發(fā)隊(duì)列):?
- ?所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的,沒有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)。
- 所有任務(wù)都在打印的syncConcurrent--- begin和syncConcurrent---end之間執(zhí)行的(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。
- 任務(wù)按順序執(zhí)行的。按順序執(zhí)行的原因:雖然并發(fā)隊(duì)列可以開啟多個(gè)線程,并且同時(shí)執(zhí)行多個(gè)任務(wù)。但是因?yàn)楸旧聿荒軇?chuàng)建新線程,只有當(dāng)前線程這一個(gè)線程(同步任務(wù)不具備開啟新線程的能力),所以也就不存在并發(fā)。而且當(dāng)前線程只有等待當(dāng)前隊(duì)列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后,才能繼續(xù)接著執(zhí)行下面的操作(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。所以任務(wù)只能一個(gè)接一個(gè)按順序執(zhí)行,不能同時(shí)被執(zhí)行。
2、異步執(zhí)行+并發(fā)隊(duì)列
可以開啟多個(gè)線程,任務(wù)交替(同時(shí))執(zhí)行。
//異步執(zhí)行+并發(fā)隊(duì)列 //特點(diǎn):可以開啟多線程,任務(wù)交替(同時(shí))執(zhí)行。 - (void)asyncConcurrent{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"asyncConcurrent---begin");dispatch_queue_t queue = dispatch_queue_create("com.lz.testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_async(queue, ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_async(queue, ^{//追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"asyncConcurrent---end");}打印結(jié)果:
2021-05-01 12:30:37.435518+0800 demo[294:6868019] currentThread---<NSThread: 0x600001700ec0>{number = 1, name = main} 2021-05-01 12:30:37.435587+0800 demo[294:6868019] asyncConcurrent---begin 2021-05-01 12:30:37.435640+0800 demo[294:6868019] asyncConcurrent---end 2021-05-01 12:30:37.463087+0800 demo[294:6868019] Metal API Validation Enabled 2021-05-01 12:30:39.435767+0800 demo[294:6868333] 1--<NSThread: 0x60000175fe80>{number = 2, name = (null)} 2021-05-01 12:30:39.435802+0800 demo[294:6868337] 3--<NSThread: 0x600001773680>{number = 4, name = (null)} 2021-05-01 12:30:39.435772+0800 demo[294:6868332] 2--<NSThread: 0x6000017004c0>{number = 3, name = (null)} 2021-05-01 12:30:41.438511+0800 demo[294:6868337] 3--<NSThread: 0x600001773680>{number = 4, name = (null)} 2021-05-01 12:30:41.438511+0800 demo[294:6868332] 2--<NSThread: 0x6000017004c0>{number = 3, name = (null)} 2021-05-01 12:30:41.438511+0800 demo[294:6868333] 1--<NSThread: 0x60000175fe80>{number = 2, name = (null)}結(jié)果分析(異步執(zhí)行+并發(fā)隊(duì)列):?
- 除了當(dāng)前線程(主線程),系統(tǒng)又開啟了3個(gè)線程,并且任務(wù)是交替/同時(shí)執(zhí)行的。(異步 執(zhí)行具備開啟新線程的能力。且并發(fā)隊(duì)列可開啟多個(gè)線程,同時(shí)執(zhí)行多個(gè)任務(wù))。
- 所有任務(wù)是在打印的asyncConcurrent--- begin和asyncConcurrent---end之后才執(zhí)行的。說明當(dāng)前線程沒有等待,而是直接開啟了新線程,在新線程中執(zhí)行任務(wù)(異步執(zhí)行不做等待,可以繼續(xù)執(zhí)行任務(wù))
3、同步執(zhí)行+串行隊(duì)列
不會開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。
//同步執(zhí)行+串行隊(duì)列 //特點(diǎn):不會開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù) - (void)syncSerial{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"syncSerial---begin");dispatch_queue_t queue = dispatch_queue_create("com.lz.testQueue", DISPATCH_QUEUE_SERIAL);dispatch_sync(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_sync(queue, ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_sync(queue, ^{//追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"syncSerial---end");}打印結(jié)果:
2021-05-02 21:29:53.162186+0800 demo[1406:6983838] currentThread---<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:29:53.162255+0800 demo[1406:6983838] syncSerial---begin 2021-05-02 21:29:55.163373+0800 demo[1406:6983838] 1--<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:29:57.163679+0800 demo[1406:6983838] 1--<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:29:59.165017+0800 demo[1406:6983838] 2--<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:30:01.165695+0800 demo[1406:6983838] 2--<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:30:03.167037+0800 demo[1406:6983838] 3--<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:30:05.168121+0800 demo[1406:6983838] 3--<NSThread: 0x600001700f80>{number = 1, name = main} 2021-05-02 21:30:05.168296+0800 demo[1406:6983838] syncSerial---end結(jié)果分析(同步執(zhí)行+串行隊(duì)列):?
- 所有任務(wù)都是在當(dāng)前線程(主線程) 中執(zhí)行的,并沒有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)。
- 所有任務(wù)都在打印的syncSerial---begin和syncSerial---end之間執(zhí)行(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。
- 任務(wù)是按順序執(zhí)行的(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)-個(gè)接一個(gè)按順序執(zhí)行)。
4、異步執(zhí)行+串行隊(duì)列
會開啟新線程,但是因?yàn)槿蝿?wù)是串行的,執(zhí)行完一一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
?
打印結(jié)果:
2021-05-02 21:39:19.551492+0800 demo[1475:6989705] currentThread---<NSThread: 0x600001710e40>{number = 1, name = main} 2021-05-02 21:39:19.551576+0800 demo[1475:6989705] asyncSerial---begin 2021-05-02 21:39:19.551638+0800 demo[1475:6989705] asyncSerial---end 2021-05-02 21:39:19.576664+0800 demo[1475:6989705] Metal API Validation Enabled 2021-05-02 21:39:21.552041+0800 demo[1475:6990008] 1--<NSThread: 0x600001771a00>{number = 2, name = (null)} 2021-05-02 21:39:23.556819+0800 demo[1475:6990008] 1--<NSThread: 0x600001771a00>{number = 2, name = (null)} 2021-05-02 21:39:25.561371+0800 demo[1475:6990008] 2--<NSThread: 0x600001771a00>{number = 2, name = (null)} 2021-05-02 21:39:27.566555+0800 demo[1475:6990008] 2--<NSThread: 0x600001771a00>{number = 2, name = (null)} 2021-05-02 21:39:29.571475+0800 demo[1475:6990008] 3--<NSThread: 0x600001771a00>{number = 2, name = (null)} 2021-05-02 21:39:31.573000+0800 demo[1475:6990008] 3--<NSThread: 0x600001771a00>{number = 2, name = (null)}結(jié)果分析(異步執(zhí)行+串行隊(duì)列):?
- 開啟了一條新線程(異步執(zhí)行具備開啟新線程的能力,串行隊(duì)列只開啟一個(gè)線程)。
- 所有任務(wù)是在打印的asyncSerial-- begin和asyncSerial---end之后才開始執(zhí)行的(異步執(zhí)行不會做任何等待,可以繼續(xù)執(zhí)行任務(wù))。
- 任務(wù)是按順序執(zhí)行的(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。
5、主隊(duì)列
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
- 所有放在主隊(duì)列中的任務(wù),都會放到主線程中執(zhí)行
- 可使用dispatch_ get_ main_ _queue0獲得主隊(duì)列
6、同步執(zhí)行+主隊(duì)列
同步執(zhí)行+主隊(duì)列在不同線程中調(diào)用結(jié)果也是不一樣,在主線程中調(diào)用會出現(xiàn)死鎖,而在其他線程中則不會。
1 、在主線程中調(diào)用同步執(zhí)行+主隊(duì)列
互相等待卡住(死鎖)
//同步執(zhí)行+主隊(duì)列 //特點(diǎn)(主線程調(diào)用):互等卡主不執(zhí)行。 //特點(diǎn)(其他線程調(diào)用):不會開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。 - (void)syncMain{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"syncMain---begin");dispatch_queue_t queue = dispatch_get_main_queue();dispatch_sync(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_sync(queue, ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_sync(queue, ^{//追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"syncMain---end");}打印結(jié)果:
?
并報(bào)錯(cuò):
結(jié)果分析(在主線程中調(diào)用同步執(zhí)行+主隊(duì)列):?
在主線程中使用同步執(zhí)行+主隊(duì)列,追加到主線程的任務(wù)1、任務(wù)2、任務(wù)3都不再執(zhí)行了,而且syncMain---end也沒有打印,在XCode 9和9以上的開發(fā)工具中還會報(bào)崩潰。這個(gè)地方我們來分析下具體原因:
上面我們提到了,所有放在主隊(duì)列中的任務(wù),都會放到主線程中執(zhí)行,而我們在主線程中執(zhí)行syncMain方法的時(shí)候,相當(dāng)于把syncMain任務(wù)放到了主線程的隊(duì)列中,我們把執(zhí)行syncMain方法命名為0號任務(wù),然后我們在執(zhí)行syncMain任務(wù)的時(shí)候,syncMain方法里面又在主隊(duì)列中追加1號任務(wù),并且是同步執(zhí)行該1號任務(wù)(同步執(zhí)行的特點(diǎn)是必須等待當(dāng)前隊(duì)列中的任務(wù)執(zhí)行完畢才會被執(zhí)行),也就是說,1號任務(wù)要想被執(zhí)行,就必須要等待0號任務(wù)(syncMain方法)執(zhí)行完畢,然而0號任務(wù)要想執(zhí)行完畢的第一步條件就是1號任務(wù)要先執(zhí)行完,所以就出現(xiàn)了互相等待,導(dǎo)致2號任務(wù)和3號任務(wù)都沒執(zhí)行,出現(xiàn)死鎖,程序崩潰。
2、在其他線程中調(diào)用同步執(zhí)行+主隊(duì)列
不會開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
//使用detachNewThreadSelector開啟新線程并自動執(zhí)行syncMain方法 [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];打印結(jié)果:
2021-05-02 22:23:40.866119+0800 demo[1793:7015378] currentThread---<NSThread: 0x600001746fc0>{number = 2, name = (null)} 2021-05-02 22:23:40.866215+0800 demo[1793:7015378] syncMain---begin 2021-05-02 22:23:40.892432+0800 demo[1793:7015081] Metal API Validation Enabled 2021-05-02 22:23:43.012675+0800 demo[1793:7015081] 1--<NSThread: 0x600001700d00>{number = 1, name = main} 2021-05-02 22:23:45.013716+0800 demo[1793:7015081] 1--<NSThread: 0x600001700d00>{number = 1, name = main} 2021-05-02 22:23:47.050584+0800 demo[1793:7015081] 2--<NSThread: 0x600001700d00>{number = 1, name = main} 2021-05-02 22:23:49.051890+0800 demo[1793:7015081] 2--<NSThread: 0x600001700d00>{number = 1, name = main} 2021-05-02 22:23:51.062918+0800 demo[1793:7015081] 3--<NSThread: 0x600001700d00>{number = 1, name = main} 2021-05-02 22:23:53.064121+0800 demo[1793:7015081] 3--<NSThread: 0x600001700d00>{number = 1, name = main} 2021-05-02 22:23:53.064335+0800 demo[1793:7015378] syncMain---end結(jié)果分析(在其他線程中調(diào)用同步執(zhí)行+主隊(duì)列):?
- 1、所有任務(wù)都是在主線程(非當(dāng)前線程)中執(zhí)行的,沒有開啟新的線程(所有放在主隊(duì)列中的任務(wù),都會放到主線程中執(zhí)行)。
- 2、所有任務(wù)都在打印的syncMain---begin和syncMain---end之間執(zhí)行(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。
- 3、任務(wù)是按順序執(zhí)行的(主隊(duì)列是串行隊(duì)列,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。
- 4、為什么現(xiàn)在就不會卡住了呢:因?yàn)閟yncMain任務(wù)放到了其他線程里,而任務(wù)1、任務(wù)2、任務(wù)3都在追加到主隊(duì)列中,這三個(gè)任務(wù)都會在主線程中執(zhí)行。syncMain 任務(wù)在其他線程中執(zhí)行到追加任務(wù)1到主隊(duì)列中,因?yàn)橹麝?duì)列現(xiàn)在沒有正在執(zhí)行的任務(wù),所以,會直接執(zhí)行主隊(duì)列的任務(wù)1,等任務(wù)1執(zhí)行完畢,再接著執(zhí)行任務(wù)2、任務(wù)3。所以這里不會卡住線程。
7、異步執(zhí)行+主隊(duì)列
只在主線程中執(zhí)行任務(wù),執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
//異步執(zhí)行+主隊(duì)列 //特點(diǎn):只在主線程中執(zhí)行任務(wù),執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù) - (void)asyncMain{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"asyncMain---begin");dispatch_queue_t queue = dispatch_get_main_queue();dispatch_async(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_async(queue, ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_async(queue, ^{//追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"asyncMain---end");}打印結(jié)果:
2021-05-04 12:28:31.356713+0800 demo[6435:7622353] currentThread---<NSThread: 0x600001710c80>{number = 1, name = main} 2021-05-04 12:28:31.356812+0800 demo[6435:7622353] asyncMain---begin 2021-05-04 12:28:31.356892+0800 demo[6435:7622353] asyncMain---end 2021-05-04 12:28:31.384916+0800 demo[6435:7622353] Metal API Validation Enabled 2021-05-04 12:28:33.497650+0800 demo[6435:7622353] 1--<NSThread: 0x600001710c80>{number = 1, name = main} 2021-05-04 12:28:35.498515+0800 demo[6435:7622353] 1--<NSThread: 0x600001710c80>{number = 1, name = main} 2021-05-04 12:28:37.498816+0800 demo[6435:7622353] 2--<NSThread: 0x600001710c80>{number = 1, name = main} 2021-05-04 12:28:39.500118+0800 demo[6435:7622353] 2--<NSThread: 0x600001710c80>{number = 1, name = main} 2021-05-04 12:28:41.501458+0800 demo[6435:7622353] 3--<NSThread: 0x600001710c80>{number = 1, name = main} 2021-05-04 12:28:43.502064+0800 demo[6435:7622353] 3--<NSThread: 0x600001710c80>{number = 1, name = main}結(jié)果分析(異步執(zhí)行+主隊(duì)列):?
- 所有任務(wù)在當(dāng)前線程(主線程)中執(zhí)行,沒有開啟新線程(雖然異步執(zhí)行具備開啟線程的能力,但因?yàn)槭侵麝?duì)列,所以所有的任務(wù)都是在主線程中)。
- 所有任務(wù)都是在打印asyncMain---begin和asyncMain---end之后執(zhí)行(異步執(zhí)行不會做任何等待,可以繼續(xù)執(zhí)行任務(wù))。
- 任務(wù)是按順序執(zhí)行(因?yàn)橹麝?duì)列是串行隊(duì)列,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)是一個(gè)接著一個(gè)執(zhí)行的)
五、GCD線程間的通信
實(shí)際開發(fā)過程中,UI的刷新是在主線程中進(jìn)行的,而一些耗時(shí)操作是放在其他線程中的(例如:上傳、下載)。當(dāng)我們在其他線程中完成耗時(shí)操作后,需要回到主線程,這時(shí)就需要線程間通信。
//線程間通信 - (void)communication{//獲取全局并發(fā)隊(duì)列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//獲取主隊(duì)列dispatch_queue_t mainQueue = dispatch_get_main_queue();dispatch_async(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}//回到主線程dispatch_async(mainQueue, ^{//追加在主線程中執(zhí)行的任務(wù)[NSThread sleepForTimeInterval:2];//耗時(shí)NSLog(@"2---%@" ,[NSThread currentThread]);//打印當(dāng)前線程});}); }打印結(jié)果:
2021-05-04 14:14:12.973897+0800 demo[6836:7668907] Metal API Validation Enabled 2021-05-04 14:14:14.953424+0800 demo[6836:7669230] 1--<NSThread: 0x600001764e00>{number = 2, name = (null)} 2021-05-04 14:14:16.956083+0800 demo[6836:7669230] 1--<NSThread: 0x600001764e00>{number = 2, name = (null)} 2021-05-04 14:14:18.956857+0800 demo[6836:7668907] 2---<NSThread: 0x600001710dc0>{number = 1, name = main}結(jié)果分析(異步執(zhí)行任務(wù)+返回主線程):
在其他線程中執(zhí)行任務(wù),執(zhí)行完后返回主線程中再執(zhí)行主線程中的操作
六、GCD其他API
1、異步柵欄+并發(fā)隊(duì)列:dispatch_barrier_async
?在我們需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_ async方法在兩個(gè)操作組間形成柵欄。dispatch_ barrier_async函數(shù)會等待前邊追加到并發(fā)隊(duì)列中的任務(wù)全部執(zhí)行完畢之后,再將指定的任
務(wù)追加到該異步隊(duì)列中。然后在di spatch_ barrier_ async函數(shù)追加的任務(wù)執(zhí)行完畢之后,異步隊(duì)列才
恢復(fù)為一般動作,接著追加任務(wù)到該異步隊(duì)列并開始執(zhí)行。
打印結(jié)果:?
2021-05-04 14:53:43.487817+0800 demo[7149:7695149] currentThread---<NSThread: 0x600001704f80>{number = 1, name = main} 2021-05-04 14:53:43.487886+0800 demo[7149:7695149] barrierAsync---begin 2021-05-04 14:53:43.487939+0800 demo[7149:7695149] barrier_async---start 2021-05-04 14:53:43.487978+0800 demo[7149:7695149] barrier_async---end 2021-05-04 14:53:43.488005+0800 demo[7149:7695149] barrier2_async---start 2021-05-04 14:53:43.488032+0800 demo[7149:7695149] barrier2_async---end 2021-05-04 14:53:43.488057+0800 demo[7149:7695149] barrier3_async---start 2021-05-04 14:53:43.488094+0800 demo[7149:7695149] barrier3_async---end 2021-05-04 14:53:43.488143+0800 demo[7149:7695149] barrierAsync---end 2021-05-04 14:53:43.513720+0800 demo[7149:7695149] Metal API Validation Enabled 2021-05-04 14:53:45.490835+0800 demo[7149:7695478] 1--<NSThread: 0x600001765680>{number = 2, name = (null)} 2021-05-04 14:53:45.490835+0800 demo[7149:7695479] 2--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:47.496184+0800 demo[7149:7695478] 1--<NSThread: 0x600001765680>{number = 2, name = (null)} 2021-05-04 14:53:47.496220+0800 demo[7149:7695479] 2--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:49.499824+0800 demo[7149:7695479] barrier--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:51.501022+0800 demo[7149:7695479] barrier--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:53.501271+0800 demo[7149:7695479] barrier2--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:55.502408+0800 demo[7149:7695479] barrier2--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:57.504119+0800 demo[7149:7695479] barrier3--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:53:59.508791+0800 demo[7149:7695479] barrier3--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:54:01.513407+0800 demo[7149:7695479] 3--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:54:01.513411+0800 demo[7149:7695492] 4--<NSThread: 0x600001774400>{number = 4, name = (null)} 2021-05-04 14:54:03.515018+0800 demo[7149:7695479] 3--<NSThread: 0x600001774080>{number = 3, name = (null)} 2021-05-04 14:54:03.515018+0800 demo[7149:7695492] 4--<NSThread: 0x600001774400>{number = 4, name = (null)}結(jié)果分析(dispatch_barrier_async):
- 在執(zhí)行柵欄之前的異步操作完成之后,才開始執(zhí)行柵欄函數(shù),最后再執(zhí)行柵欄函數(shù)之后的異步任務(wù);
- 由于是異步任務(wù),和異步柵欄,所有在執(zhí)行任務(wù)執(zhí)行先打印barrier---begin、dispatch_barrier_async---start、dispatch_barrier_async---end、barrier---end
- 柵欄之前和柵欄之后的任務(wù)是并發(fā)執(zhí)行的(并發(fā)隊(duì)列),并開啟了新線程
- 多個(gè)柵欄函數(shù)串行執(zhí)行
2、同步柵欄+并發(fā)隊(duì)列:dispatch_barrier_sync
?同步柵欄是在主線程中按順序執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)(串行執(zhí)行);
柵欄函數(shù)之前任務(wù)執(zhí)行完后再執(zhí)行柵欄,最后再執(zhí)行柵欄之后的任務(wù);
//同步柵欄+并發(fā)隊(duì)列 //特點(diǎn):同步柵欄是在主線程中串行執(zhí)行;柵欄函數(shù)之前任務(wù)執(zhí)行完后再執(zhí)行柵欄,最后再執(zhí)行柵欄之后的任務(wù); - (void)barrierSync{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"barrierSync---begin");dispatch_queue_t queue = dispatch_queue_create("com.lz.testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_async(queue, ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"barrier_async---start");dispatch_barrier_sync(queue, ^{//追加任務(wù)barrierfor (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"barrier--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"barrier_sync---end");NSLog(@"barrier2_sync---start");dispatch_barrier_sync(queue, ^{//追加任務(wù)barrierfor (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"barrier2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"barrier2_sync---end");NSLog(@"barrier3_sync---start");dispatch_barrier_sync(queue, ^{//追加任務(wù)barrierfor (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"barrier3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"barrier_sync---end");dispatch_async(queue, ^{//追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_async(queue, ^{//追加任務(wù)4for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"4--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});NSLog(@"barrierAsync---end"); }?打印結(jié)果:
2021-05-04 14:58:47.977351+0800 demo[7185:7698406] currentThread---<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:58:47.977419+0800 demo[7185:7698406] barrierSync---begin 2021-05-04 14:58:47.977469+0800 demo[7185:7698406] barrier_async---start 2021-05-04 14:58:49.980344+0800 demo[7185:7698730] 1--<NSThread: 0x600001748340>{number = 3, name = (null)} 2021-05-04 14:58:49.980362+0800 demo[7185:7698737] 2--<NSThread: 0x60000175c700>{number = 2, name = (null)} 2021-05-04 14:58:51.985759+0800 demo[7185:7698730] 1--<NSThread: 0x600001748340>{number = 3, name = (null)} 2021-05-04 14:58:51.985764+0800 demo[7185:7698737] 2--<NSThread: 0x60000175c700>{number = 2, name = (null)} 2021-05-04 14:58:53.986021+0800 demo[7185:7698406] barrier--<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:58:55.986319+0800 demo[7185:7698406] barrier--<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:58:55.986485+0800 demo[7185:7698406] barrier_sync---end 2021-05-04 14:58:55.986555+0800 demo[7185:7698406] barrier2_sync---start 2021-05-04 14:58:57.987788+0800 demo[7185:7698406] barrier2--<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:58:59.989132+0800 demo[7185:7698406] barrier2--<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:58:59.989305+0800 demo[7185:7698406] barrier2_sync---end 2021-05-04 14:58:59.989373+0800 demo[7185:7698406] barrier3_sync---start 2021-05-04 14:59:01.990530+0800 demo[7185:7698406] barrier3--<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:59:03.990899+0800 demo[7185:7698406] barrier3--<NSThread: 0x600001714e40>{number = 1, name = main} 2021-05-04 14:59:03.991056+0800 demo[7185:7698406] barrier_sync---end 2021-05-04 14:59:03.991151+0800 demo[7185:7698406] barrierAsync---end 2021-05-04 14:59:04.023463+0800 demo[7185:7698406] Metal API Validation Enabled 2021-05-04 14:59:05.996299+0800 demo[7185:7698736] 3--<NSThread: 0x60000176d700>{number = 4, name = (null)} 2021-05-04 14:59:05.996311+0800 demo[7185:7698815] 4--<NSThread: 0x6000017739c0>{number = 5, name = (null)} 2021-05-04 14:59:07.999438+0800 demo[7185:7698815] 4--<NSThread: 0x6000017739c0>{number = 5, name = (null)} 2021-05-04 14:59:07.999438+0800 demo[7185:7698736] 3--<NSThread: 0x60000176d700>{number = 4, name = (null)}結(jié)果分析:
- 同步柵欄會在執(zhí)行完任務(wù)后才執(zhí)行下一個(gè)任務(wù),即先打印barrier_async---start,然后會等待執(zhí)行完柵欄任務(wù)后,再打印barrier_sync---end
- 執(zhí)行完柵欄之前的任務(wù)后才執(zhí)行柵欄任務(wù),最后再執(zhí)行柵欄之后的異步任務(wù)
- 同步柵欄的任務(wù)都是在主線程中按順序執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
注意:在使用柵欄函數(shù)時(shí).使用自定義隊(duì)列才有意義,如果用的是串行隊(duì)列或者系統(tǒng)提供的全局并發(fā)隊(duì)列,這個(gè)柵欄函數(shù)的作用等同于一個(gè)同步函數(shù)的作用(這里就不做舉例了,實(shí)際開發(fā)中一般不這樣使用,如果是在主線程中調(diào)用同步串行同樣會死鎖);
3、GCD的延遲執(zhí)行方法:dispatch_after
?1、在指定時(shí)間之后執(zhí)行某個(gè)任務(wù)
//延遲執(zhí)行 - (void)after{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"barrierSync---begin");dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 2.0 * NSEC_PER_SEC ), dispatch_get_main_queue(), ^{//2秒后追加任務(wù)到主隊(duì)列開始執(zhí)行NSLog(@"after--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}); }打印結(jié)果:
2021-05-04 15:20:09.804974+0800 demo[7306:7710342] currentThread---<NSThread: 0x600001714e00>{number = 1, name = main} 2021-05-04 15:20:09.805044+0800 demo[7306:7710342] barrierSync---begin 2021-05-04 15:20:09.831556+0800 demo[7306:7710342] Metal API Validation Enabled 2021-05-04 15:20:11.974272+0800 demo[7306:7710342] after--<NSThread: 0x600001714e00>{number = 1, name = main}結(jié)果分析:
在打印barrierSync---begin 2秒后才打印after--<NSThread: 0x600001714e00>{number = 1, name = main}
注意:dispatch_after并不是在指定時(shí)間后開始執(zhí)行處理,而是在指定時(shí)間之后將任務(wù)追加到某個(gè)隊(duì)列中(本例是將任務(wù)追加到主隊(duì)列中)。所有嚴(yán)格意義來講,這個(gè)時(shí)間并不是絕對的準(zhǔn)確(拿本里來說,如果在2秒后追加到主隊(duì)列中后,主隊(duì)列中還有耗時(shí)操作在執(zhí)行,那添加到主隊(duì)列中的這個(gè)任務(wù)就會遲遲不執(zhí)行),但想要大致延遲執(zhí)行任務(wù),此方法還是很有效的。
2、dispatch_after執(zhí)行時(shí)間不準(zhǔn)確證明:
我們在主線程中調(diào)用after1方法后再添加一段耗時(shí)操作,并添加相應(yīng)的時(shí)間日志:
[self after1];NSLog(@"耗時(shí)begin---:%@",[NSDate date]);for (NSInteger i=0; i<4; i++) {[NSThread sleepForTimeInterval:1.0];}NSLog(@"耗時(shí)end---:%@",[NSDate date]); //延遲執(zhí)行 - (void)after1{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"barrierSync---begin");NSLog(@"begin---:%@",[NSDate date]);dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 2 * NSEC_PER_SEC ), dispatch_get_main_queue(), ^{//2秒后追加任務(wù)到主隊(duì)列開始執(zhí)行NSLog(@"after---:%@",[NSDate date]);NSLog(@"after--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}); }打印結(jié)果:
2021-05-04 15:35:31.358778+0800 demo[7379:7718375] currentThread---<NSThread: 0x60000170ce40>{number = 1, name = main} 2021-05-04 15:35:31.358855+0800 demo[7379:7718375] barrierSync---begin 2021-05-04 15:35:31.358934+0800 demo[7379:7718375] begin---:Tue May 4 15:35:31 2021 2021-05-04 15:35:31.360508+0800 demo[7379:7718375] 耗時(shí)begin---:2021-05-04 07:35:31 +0000 2021-05-04 15:35:35.362701+0800 demo[7379:7718375] 耗時(shí)end---:2021-05-04 07:35:35 +0000 2021-05-04 15:35:35.432347+0800 demo[7379:7718375] Metal API Validation Enabled 2021-05-04 15:35:35.501987+0800 demo[7379:7718375] after---:Tue May 4 15:35:35 2021 2021-05-04 15:35:35.502076+0800 demo[7379:7718375] after--<NSThread: 0x60000170ce40>{number = 1, name = main}結(jié)果分析:
begin---:Tue May? 4 15:35:31 2021
耗時(shí)begin---:2021-05-04 07:35:31 +0000
耗時(shí)end---:2021-05-04 07:35:35 +0000
after---:Tue May? 4 15:35:35 2021
從順序上可以看出:
代碼中是在2秒后追加任務(wù)到主隊(duì)列,然后此時(shí)主隊(duì)列中的耗時(shí)操作剛好執(zhí)行到2秒,還剩2秒,所有當(dāng)主隊(duì)列中的4秒耗時(shí)操作的任務(wù)執(zhí)行完時(shí),才開始執(zhí)行2秒前追到主隊(duì)列中的任務(wù),所有此時(shí)after---打印結(jié)果剛好是延遲4秒的結(jié)果
結(jié)論:dispatch_after是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中的,并不是在指定時(shí)間后執(zhí)行任務(wù)
4、GCD一次性代碼:dispatch_once
經(jīng)常用此方法來實(shí)現(xiàn)單利?
//一次性代碼(只執(zhí)行一次) - (void)once{static dispatch_once_t once;dispatch_once(&once, ^{//只執(zhí)行一次的代碼NSLog(@"只執(zhí)行一次的代碼");}); }5、GCD快速迭代方法:dispatch_apply
通常我們會用for循環(huán)遍歷,但是GCD給我們提供了快速迭代的函數(shù)dispatch_ apply。dispatch_apply按照指定的次數(shù)將指定的任務(wù)追加到指定的隊(duì)列中,并等待全部隊(duì)列執(zhí)行結(jié)束。如果是在串行隊(duì)列中使用dispatch_apply, 那么就和for循環(huán)一樣,按順序同步執(zhí)行。可這樣就體現(xiàn)不出快速迭代的意義了。
我們可以利用并發(fā)隊(duì)列進(jìn)行異步執(zhí)行。比如說遍歷0~5這6個(gè)數(shù)字,for 循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷。dispatch_apply 可以在多個(gè)線程中同時(shí)(異步)遍歷多個(gè)數(shù)字。
還有一點(diǎn),無論是在串行隊(duì)列,還是異步隊(duì)列中,dispatch_apply 都會等待全部任務(wù)執(zhí)行完畢,這點(diǎn)就像是同步操作,也像是隊(duì)列組中的dispatch_group_wait方法。
打印結(jié)果:
2021-05-04 16:59:22.875336+0800 demo[7729:7756107] currentThread---<NSThread: 0x600001714dc0>{number = 1, name = main} 2021-05-04 16:59:22.875405+0800 demo[7729:7756107] apply---begin 2021-05-04 16:59:22.875500+0800 demo[7729:7756107] 2 -- <NSThread: 0x600001714dc0>{number = 1, name = main} 2021-05-04 16:59:22.875528+0800 demo[7729:7756442] 1 -- <NSThread: 0x600001754400>{number = 4, name = (null)} 2021-05-04 16:59:22.875528+0800 demo[7729:7756447] 3 -- <NSThread: 0x600001758a80>{number = 3, name = (null)} 2021-05-04 16:59:22.875534+0800 demo[7729:7756441] 0 -- <NSThread: 0x600001744080>{number = 2, name = (null)} 2021-05-04 16:59:22.875564+0800 demo[7729:7756449] 4 -- <NSThread: 0x600001748400>{number = 5, name = (null)} 2021-05-04 16:59:22.875606+0800 demo[7729:7756450] 5 -- <NSThread: 0x600001760fc0>{number = 6, name = (null)} 2021-05-04 16:59:22.875676+0800 demo[7729:7756107] apply---end結(jié)果分析:
- 因?yàn)槭窃诓l(fā)隊(duì)列中異步執(zhí)行任務(wù),所以各個(gè)任務(wù)的執(zhí)行時(shí)間長短不定,最后結(jié)束順序也不定。
- 但是apply---end 一定在最后執(zhí)行。這是因?yàn)閐ispatch_apply函數(shù)會等待全部任務(wù)執(zhí)行完畢。
- 在多個(gè)線程中遍歷打印
- 隨著手機(jī)的多核,同樣的for循環(huán)遍歷,顯然使用dispatch_apply執(zhí)行效率會提高。
- 如果在dispatch_apply外再包一層異步任務(wù),那么就和dispatch_group效果一樣了。
6、GCD隊(duì)列組:dispatch_group
有時(shí)候我們會有這樣的需求:分別異步執(zhí)行2個(gè)耗時(shí)任務(wù),然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時(shí)候我們可以用到GCD的隊(duì)列組。
調(diào)用隊(duì)列組的dispatch_group_async先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中。或者使用隊(duì)列組的dispatch_group_enter、dispatch_group_leave組合來實(shí)現(xiàn)dispatch_ group_ async。
調(diào)用隊(duì)列組的dispatch_group_notify回到指定線程執(zhí)行任務(wù)。或者使用dispatch_group_wait回到當(dāng)前線程繼續(xù)向下執(zhí)行(會阻塞當(dāng)前線程)。?
1、dispatch_group_notify
?監(jiān)聽group中任務(wù)的完成狀態(tài),當(dāng)所有的任務(wù)都執(zhí)行完成后,追加任務(wù)到group 中,并執(zhí)行任務(wù)。
//隊(duì)列組:dispatch_group_notify - (void)groupNotify{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"groupNotify---begin");dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_group_notify(group, dispatch_get_main_queue(), ^{//等待任務(wù)1、2執(zhí)行完后,回到主線程中執(zhí)行任務(wù)3//主線程追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}NSLog(@"groupNotify---end");}); }打印結(jié)果:?
2021-05-04 17:23:54.610572+0800 demo[7883:7772333] currentThread---<NSThread: 0x600001709000>{number = 1, name = main} 2021-05-04 17:23:54.610640+0800 demo[7883:7772333] groupNotify---begin 2021-05-04 17:23:54.635443+0800 demo[7883:7772333] Metal API Validation Enabled 2021-05-04 17:23:56.615585+0800 demo[7883:7772692] 2--<NSThread: 0x600001772c00>{number = 3, name = (null)} 2021-05-04 17:23:56.615584+0800 demo[7883:7772696] 1--<NSThread: 0x600001774f40>{number = 2, name = (null)} 2021-05-04 17:23:58.615805+0800 demo[7883:7772692] 2--<NSThread: 0x600001772c00>{number = 3, name = (null)} 2021-05-04 17:23:58.615824+0800 demo[7883:7772696] 1--<NSThread: 0x600001774f40>{number = 2, name = (null)} 2021-05-04 17:24:00.616600+0800 demo[7883:7772333] 3--<NSThread: 0x600001709000>{number = 1, name = main} 2021-05-04 17:24:02.616919+0800 demo[7883:7772333] 3--<NSThread: 0x600001709000>{number = 1, name = main} 2021-05-04 17:24:02.617066+0800 demo[7883:7772333] groupNotify---end結(jié)果分析(隊(duì)列組dispatch_group_notify):
當(dāng)所有任務(wù)都執(zhí)行完成之后,才執(zhí)行dispatch_group_notify的block中的任務(wù)。
2、dispatch_group_wait
暫停當(dāng)前線程(阻塞當(dāng)前線程),等待指定的group 中的任務(wù)執(zhí)行完成后,才會往下繼續(xù)執(zhí)行。
//隊(duì)列組:dispatch_group_wait - (void)groupWait{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"groupWait---begin");dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}});dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(@"groupWait---end"); }打印結(jié)果:?
2021-05-04 17:29:10.742494+0800 demo[7931:7776937] currentThread---<NSThread: 0x60000170ce40>{number = 1, name = main} 2021-05-04 17:29:10.742576+0800 demo[7931:7776937] groupWait---begin 2021-05-04 17:29:12.746907+0800 demo[7931:7777277] 1--<NSThread: 0x6000017538c0>{number = 2, name = (null)} 2021-05-04 17:29:12.746923+0800 demo[7931:7777271] 2--<NSThread: 0x60000174d4c0>{number = 3, name = (null)} 2021-05-04 17:29:14.748485+0800 demo[7931:7777271] 2--<NSThread: 0x60000174d4c0>{number = 3, name = (null)} 2021-05-04 17:29:14.748485+0800 demo[7931:7777277] 1--<NSThread: 0x6000017538c0>{number = 2, name = (null)} 2021-05-04 17:29:14.748808+0800 demo[7931:7776937] groupWait---end結(jié)果分析(隊(duì)列組dispatch_group_wait):
當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行dispatch_group_wait 之后的操作。但是使用dispatch_group_wait 會阻塞當(dāng)前線程。
3、dispatch_group_enter、dispatch_group_leave
dispatch_group_enter 標(biāo)志著一個(gè)任務(wù)追加到 group, 執(zhí)行一次,相當(dāng)于group中未執(zhí)行完畢任務(wù)數(shù)+1;
dispatch_group_leave標(biāo)志著一個(gè)任務(wù)離開了group, 執(zhí)行一次,相當(dāng)于group中未執(zhí)行完畢任務(wù)數(shù)-1。
當(dāng)group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會使dispatch_group_wait解除阻塞,以及執(zhí)行追加到dispatch_group_notify中的任務(wù)。
//隊(duì)列組:dispatch_group_enter dispatch_group_leave - (void)groupEnterAndLeave{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"groupWait---begin");dispatch_group_t group = dispatch_group_create();dispatch_group_enter(group);dispatch_async(dispatch_get_global_queue(0, 0), ^{//追加任務(wù)1for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"1--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}dispatch_group_leave(group);});dispatch_group_enter(group);dispatch_async(dispatch_get_global_queue(0, 0), ^{//追加任務(wù)2for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"2--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}dispatch_group_leave(group);});dispatch_group_notify(group, dispatch_get_main_queue(), ^{//等待任務(wù)1、2執(zhí)行完后,回到主線程中執(zhí)行任務(wù)3//主線程追加任務(wù)3for (int i = 0; i<2; i++) {[NSThread sleepForTimeInterval:2]; //耗時(shí)NSLog(@"3--%@" ,[NSThread currentThread]);//打印當(dāng)前線程}NSLog(@"groupNotify---end");});// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // NSLog(@"groupWait---end"); }打印結(jié)果:
2021-05-04 17:38:09.330827+0800 demo[8012:7783864] currentThread---<NSThread: 0x60000170ce00>{number = 1, name = main} 2021-05-04 17:38:09.330916+0800 demo[8012:7783864] groupWait---begin 2021-05-04 17:38:09.367985+0800 demo[8012:7783864] Metal API Validation Enabled 2021-05-04 17:38:11.335041+0800 demo[8012:7784186] 1--<NSThread: 0x60000176c540>{number = 2, name = (null)} 2021-05-04 17:38:11.335040+0800 demo[8012:7784185] 2--<NSThread: 0x60000176ba40>{number = 3, name = (null)} 2021-05-04 17:38:13.340338+0800 demo[8012:7784185] 2--<NSThread: 0x60000176ba40>{number = 3, name = (null)} 2021-05-04 17:38:13.340338+0800 demo[8012:7784186] 1--<NSThread: 0x60000176c540>{number = 2, name = (null)} 2021-05-04 17:38:15.340748+0800 demo[8012:7783864] 3--<NSThread: 0x60000170ce00>{number = 1, name = main} 2021-05-04 17:38:17.342019+0800 demo[8012:7783864] 3--<NSThread: 0x60000170ce00>{number = 1, name = main} 2021-05-04 17:38:17.342190+0800 demo[8012:7783864] groupNotify---end結(jié)果分析:
- 當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行dispatch_group_notify 中的任務(wù)。這里的dispatch_group_enter、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async。
- 有了dispatch_group_async為什么還需要dispatch_group_enter、dispatch_group_leave組合呢?
- 這是因?yàn)?#xff0c;實(shí)際的使用場景中,很大可能是一堆異步回調(diào),并不是一個(gè)dispatch_group_async,比如AFN封裝的網(wǎng)絡(luò)請求,就是一個(gè)異步回調(diào),并不是一個(gè)dispatch_group_async,要想實(shí)現(xiàn)網(wǎng)絡(luò)請求的組合,就需要用到dispatch_group_enter、dispatch_group_leave;
7、GCD信號量:
GCD中的信號量是指Dispatch Semaphore,是持有計(jì)數(shù)的信號。類似于過高速路收費(fèi)站的欄桿。可以通過時(shí),打開欄桿,不可以通過時(shí),關(guān)閉欄桿。在Dispatch Semaphore中,使用計(jì)數(shù)來完成這個(gè)功能,計(jì)數(shù)為0時(shí)等待,不可通過。計(jì)數(shù)為1或大于1時(shí),計(jì)數(shù)減1且不等待,可通過。Dispatch Semaphore提供了三個(gè)函數(shù)。
- dispatch_semaphore_create: 創(chuàng)建一個(gè)Semaphore并初始化信號的總量
- dispatch_semaphore_signal: 發(fā)送一個(gè)信號, 讓信號總量加1
- dispatch_semaphore_wait: 可以使總信號量減1,當(dāng)信號總量為0時(shí)就會一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
注意:信號量的使用前提是:想清楚你需要處理哪個(gè)線程等待(阻塞), 又要哪個(gè)線程繼續(xù)執(zhí)行,然后使用信號量。
Dispatch Semaphore在實(shí)際開發(fā)中主要用于:
- 保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
- 保證線程安全,為線程加鎖
1、Dispatch Semaphore線程同步
我們在開發(fā)中,會遇到這樣的需求:|異步執(zhí)行耗時(shí)任務(wù),并使用異步執(zhí)行的結(jié)果進(jìn)行一些額外的操作。換句話說,相當(dāng)于,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。
- (void)semaphoreSync{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"semaphoreSync---begin");dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);__block int number = 0;dispatch_async(queue, ^{//追加任務(wù)1[NSThread sleepForTimeInterval:2];NSLog(@"1---%@" ,[NSThread currentThread]);number=100;dispatch_semaphore_signal(semaphore);});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);NSLog(@"semaphoreSync---end ,number = %d" ,number); }打印結(jié)果:?
2021-05-04 20:25:50.925356+0800 demo[8657:7845011] currentThread---<NSThread: 0x600001708f00>{number = 1, name = main} 2021-05-04 20:25:50.925422+0800 demo[8657:7845011] semaphoreSync---begin 2021-05-04 20:25:52.928148+0800 demo[8657:7845353] 1---<NSThread: 0x600001755940>{number = 2, name = (null)} 2021-05-04 20:25:52.928336+0800 demo[8657:7845011] semaphoreSync---end ,number = 100結(jié)果分析:
semaphore---end是在執(zhí)行完number = 100之后才打印的。而且輸出結(jié)果number 為100。這是因?yàn)楫惒綀?zhí)行不會做任何等待,可以繼續(xù)執(zhí)行任務(wù)。異步執(zhí)行將任務(wù)1追加到隊(duì)列之后,不做等待,接著執(zhí)行di spatch_ semaphore_ wait方法。此時(shí)semaphore == 0,當(dāng)前線程進(jìn)入等待狀態(tài)。然后異步任務(wù)1開始執(zhí)行。任務(wù)1執(zhí)行到dispatch_semaphore_signal之后, 此時(shí)總信號量semaphore == 1,然后dispatch_semaphore_wait方法使總信號量減1,正在被阻塞的線程(主線程)恢復(fù)繼續(xù)執(zhí)行。最后打印semaphoreSync---end ,number = 100。 這樣就實(shí)現(xiàn)了線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。
2、Dispatch Semaphore線程安全和線程同步(為線程加鎖)
?
- 1、什么是線程安全
如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣
的,就是線程安全的。若每個(gè)線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作(更改變量),一般都需要考慮線程同步,否則的話就可能影響線程安全。
- 2、什么是線程同步
可理解為線程A和線程B 一塊配合,A執(zhí)行到一定程度時(shí)要依靠線程B的某個(gè)結(jié)果,于是停下來,示意B運(yùn)行; B依言執(zhí)行,再將結(jié)果給A; A再繼續(xù)操作。
舉個(gè)簡單例子就是:
兩個(gè)人在一起聊天。兩個(gè)人不能同時(shí)說話,避免聽不清(操作沖突)。等一個(gè)人說完(一個(gè)線程結(jié)束操作),另一個(gè)再說(另一個(gè)線程再開始操作)。
下面,我們模擬火車票售賣的方式,實(shí)現(xiàn)NSThread線程安全和解決線程同步問題。
場景:總共有50張火車票,有兩個(gè)售賣火車票的窗口,一個(gè)是北京火車票售賣窗口,另一個(gè)是西安火車票售賣窗口。兩個(gè)窗口同時(shí)售賣火車票,賣完為止。
@interface ViewController(){dispatch_semaphore_t semaphoreLock; } @property(assign ,nonatomic) NSInteger ticketCount; @end //線程安全:使用semaphore枷鎖 //初始化火車票數(shù)量、賣票窗口(線程安全)、并開始賣票 - (void)initTacket{NSLog(@"currentThread---%@" , [NSThread currentThread]); //打印當(dāng)前線程N(yùn)SLog(@"semaphore---begin");semaphoreLock = dispatch_semaphore_create(1);self.ticketCount = 50;//queue1 北京售賣窗口 queue2西安售賣窗口dispatch_queue_t queue1 = dispatch_queue_create("com.beijin.testQueue1", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue2 = dispatch_queue_create("com.xian.testQueue2", DISPATCH_QUEUE_SERIAL);dispatch_async(queue1, ^{[self saleTacket];});dispatch_async(queue2, ^{[self saleTacket];}); }- (void)saleTacket{while (1) {//相當(dāng)于加鎖dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);if(self.ticketCount>0){//如果有票,繼續(xù)售賣self.ticketCount--;NSLog(@"剩余票數(shù):%ld,窗口:%@" ,(long)self.ticketCount ,[NSThread currentThread]);[NSThread sleepForTimeInterval:2];}else{//如果已售完,關(guān)閉售賣窗口NSLog(@"所有火車票均已經(jīng)售完");//相當(dāng)于解鎖dispatch_semaphore_signal(semaphoreLock);break;}//相當(dāng)于解鎖dispatch_semaphore_signal(semaphoreLock);} }打印結(jié)果:?
2021-05-04 21:02:19.678510+0800 demo[8863:7866145] currentThread---<NSThread: 0x600001705180>{number = 1, name = main} 2021-05-04 21:02:19.678580+0800 demo[8863:7866145] semaphore---begin 2021-05-04 21:02:19.678753+0800 demo[8863:7866484] 剩余票數(shù):49,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:19.705312+0800 demo[8863:7866145] Metal API Validation Enabled 2021-05-04 21:02:21.681344+0800 demo[8863:7866480] 剩余票數(shù):48,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:23.684746+0800 demo[8863:7866484] 剩余票數(shù):47,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:25.688437+0800 demo[8863:7866480] 剩余票數(shù):46,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:27.691144+0800 demo[8863:7866484] 剩余票數(shù):45,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:29.693793+0800 demo[8863:7866480] 剩余票數(shù):44,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:31.694281+0800 demo[8863:7866484] 剩余票數(shù):43,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:33.694617+0800 demo[8863:7866480] 剩余票數(shù):42,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:35.699981+0800 demo[8863:7866484] 剩余票數(shù):41,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:37.704744+0800 demo[8863:7866480] 剩余票數(shù):40,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:39.708801+0800 demo[8863:7866484] 剩余票數(shù):39,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:41.713472+0800 demo[8863:7866480] 剩余票數(shù):38,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:43.716771+0800 demo[8863:7866484] 剩余票數(shù):37,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:45.721850+0800 demo[8863:7866480] 剩余票數(shù):36,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:47.723082+0800 demo[8863:7866484] 剩余票數(shù):35,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:49.723433+0800 demo[8863:7866480] 剩余票數(shù):34,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:51.724468+0800 demo[8863:7866484] 剩余票數(shù):33,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:53.724734+0800 demo[8863:7866480] 剩余票數(shù):32,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:55.724944+0800 demo[8863:7866484] 剩余票數(shù):31,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:02:57.726290+0800 demo[8863:7866480] 剩余票數(shù):30,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:02:59.726500+0800 demo[8863:7866484] 剩余票數(shù):29,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:01.727866+0800 demo[8863:7866480] 剩余票數(shù):28,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:03.728177+0800 demo[8863:7866484] 剩余票數(shù):27,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:05.733521+0800 demo[8863:7866480] 剩余票數(shù):26,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:07.738426+0800 demo[8863:7866484] 剩余票數(shù):25,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:09.738757+0800 demo[8863:7866480] 剩余票數(shù):24,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:11.739391+0800 demo[8863:7866484] 剩余票數(shù):23,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:13.741630+0800 demo[8863:7866480] 剩余票數(shù):22,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:15.742803+0800 demo[8863:7866484] 剩余票數(shù):21,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:17.743079+0800 demo[8863:7866480] 剩余票數(shù):20,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:19.748496+0800 demo[8863:7866484] 剩余票數(shù):19,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:31.748573+0800 demo[8863:7866480] 剩余票數(shù):18,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:33.748988+0800 demo[8863:7866484] 剩余票數(shù):17,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:35.749469+0800 demo[8863:7866480] 剩余票數(shù):16,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:03:44.908523+0800 demo[8863:7866484] 剩余票數(shù):15,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:03:55.109256+0800 demo[8863:7866480] 剩余票數(shù):14,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:04:07.109335+0800 demo[8863:7866484] 剩余票數(shù):13,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:04:17.868055+0800 demo[8863:7866480] 剩余票數(shù):12,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:04:29.868121+0800 demo[8863:7866484] 剩余票數(shù):11,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:04:38.937986+0800 demo[8863:7866480] 剩余票數(shù):10,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:04:44.730980+0800 demo[8863:7866484] 剩余票數(shù):9,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:04:46.731503+0800 demo[8863:7866480] 剩余票數(shù):8,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:04:48.731708+0800 demo[8863:7866484] 剩余票數(shù):7,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:04:50.731975+0800 demo[8863:7866480] 剩余票數(shù):6,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:04:52.993830+0800 demo[8863:7866484] 剩余票數(shù):5,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:04:54.994015+0800 demo[8863:7866480] 剩余票數(shù):4,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:04:59.880925+0800 demo[8863:7866484] 剩余票數(shù):3,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:05:07.810866+0800 demo[8863:7866480] 剩余票數(shù):2,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:05:09.811191+0800 demo[8863:7866484] 剩余票數(shù):1,窗口:<NSThread: 0x600001739880>{number = 2, name = (null)} 2021-05-04 21:05:11.812873+0800 demo[8863:7866480] 剩余票數(shù):0,窗口:<NSThread: 0x60000176eb80>{number = 3, name = (null)} 2021-05-04 21:05:13.817595+0800 demo[8863:7866484] 所有火車票均已經(jīng)售完 2021-05-04 21:05:13.817868+0800 demo[8863:7866480] 所有火車票均已經(jīng)售完結(jié)果分析:
從打印結(jié)果上來看,2號窗口和3號窗口是交替出現(xiàn)的,實(shí)際上2號窗口和3號窗口是有可能不是交替售賣的。
從結(jié)果上看,是線程安全的。
七、GCD源碼解讀之基于UNIX的操作系統(tǒng)內(nèi)核
要了解GCD源碼,首先得了解UNIX的操作系統(tǒng)內(nèi)核,這里我們只簡單了解概念,不深究(可參考https://www.freebsd.org)
?
1、Darwin
? ? ?接口的易用性是Mac OS X的基石,以UNIX為系統(tǒng)內(nèi)核使Darwin具有極佳的穩(wěn)定性、可靠性和性能。Darwin擁有許多技術(shù):Mach 3.0、基于FreeBSD 5的操作系統(tǒng)服務(wù)環(huán)境、高性能的網(wǎng)絡(luò)設(shè)施和多文件系統(tǒng)支持。Darwin具有模塊化的特征,可動態(tài)添加和卸載驅(qū)動程序,還有良好的網(wǎng)絡(luò)擴(kuò)展性。
2、Mach
Mach是Darwin的中心。它提供了操作系統(tǒng)的許多重要功能,這些功能對應(yīng)用程序而言是透明的。它處理CPU的使用和內(nèi)存管理,系統(tǒng)調(diào)度,內(nèi)存保護(hù),實(shí)現(xiàn)了“以消息為中心”的架構(gòu),用于實(shí)現(xiàn)多進(jìn)程信息傳遞。它為系統(tǒng)提供:
- ?內(nèi)存保護(hù)。一個(gè)進(jìn)程如果意外的將數(shù)據(jù)寫入系統(tǒng)的其它進(jìn)程,會導(dǎo)致數(shù)據(jù)丟失或系統(tǒng)崩潰。Mach避免這種情況的發(fā)生,Mach讓系統(tǒng)的一部分不會破壞其它的部分。
- ?搶占式多任務(wù)。Mach監(jiān)視CPU,調(diào)度任務(wù)(task)的運(yùn)行,保證系統(tǒng)的高效率和資源利用。由系統(tǒng)決定一個(gè)任務(wù)的執(zhí)行時(shí)間,而不是另一進(jìn)程
- ?虛擬內(nèi)存。每個(gè)進(jìn)程有自己的唯一的地址空間。32位應(yīng)用程序有4GB的地址空間,64位應(yīng)用程序有18EB的地址空間。Mach處理從任務(wù)中虛內(nèi)存到物理內(nèi)存的映射。在任意時(shí)刻,只有一部分虛內(nèi)存實(shí)際駐留在物理內(nèi)存中。物理內(nèi)存在需要時(shí)被換入(page in)。Mach使用內(nèi)存對象(memory objects)的概念讓一個(gè)任務(wù)映射一部分內(nèi)存,逆映射,并送入另一任務(wù)
- ?實(shí)時(shí)支持。對時(shí)間敏感的應(yīng)用程序保證低延遲。
Mach也支持合作多任務(wù)(cooperative multitasking),強(qiáng)占式線程(preemptive threading)和合作線程(cooperative threading)。
驅(qū)動程序支持
????????Darwin為驅(qū)動程序開發(fā)提供了面向?qū)ο蟮钠脚_,I/O Kit。它簡化了在Mac OS X上的驅(qū)動程序開發(fā),提供了一套良好的架構(gòu)。I/O Kit由C++的子集寫成。用于支持一套驅(qū)動程序設(shè)備家族,并且它是可擴(kuò)展的。它提供了:
?
- ?即插即用(在設(shè)備插入時(shí)即可用)
- ?動態(tài)設(shè)備管理(如設(shè)備如何處理突然斷電的情況)
- ?電源管理
????????I/O Kit提供了編寫與標(biāo)準(zhǔn)不符或沒有標(biāo)準(zhǔn)的設(shè)備的驅(qū)動程序所需的資源。這些設(shè)備包括AGP卡,PCI和PCIe卡,掃描儀等通常需要自定驅(qū)動的設(shè)備。如何創(chuàng)建驅(qū)動程序,請看I/O Kit 設(shè)備驅(qū)動程序設(shè)計(jì)指南。
3、BSD
BSD與Mach結(jié)合成為內(nèi)核的核心部分,它提供:
- ?進(jìn)程模型(進(jìn)程ID,信號。。)
- ?基礎(chǔ)的安全策略(文件權(quán)限設(shè)定和用戶和組ID設(shè)定)
- ?線程支持(POSIX線程)
- ?網(wǎng)絡(luò)支持(BSD套接字)
八、GCD源碼解讀之mach-o
Mach-O是一種文件格式,是mac上可執(zhí)行文件的格式,我們編寫的C、C++、swift、OC,最終編譯鏈接生成Mach-O可執(zhí)行文件。
1、mach-o文件類型分為(總共有11種):
1、MH_OBJECT (0x1 )
- 目標(biāo)文件(.0)
- 靜態(tài)庫文件(.a),靜態(tài)庫文件其實(shí)就是多個(gè).o文件的合集.比如支持多種cpu建構(gòu)的.a庫文件.
2、MH_EXECUTE (0x2) 可執(zhí)行文件
- 比如.app文件
3、MH_DYLIB 動態(tài)庫文件
- .dylib文件
- .framework/xx文件
4、MH_DYLINKER (0x7) 動態(tài)鏈接編輯器
- usr/lib/dyld
5、MH_DYSM 符號文件
- dSYM/Content/Resources/DWARF/xx常用與app崩潰信息分析
2、Mach-O格式
Mach-O是一個(gè)以數(shù)據(jù)塊分組的二進(jìn)制字節(jié)流,這些數(shù)據(jù)塊包含元信息,比如字節(jié)順序、CPU類型、數(shù)據(jù)塊大小等等。
典型的Mach-O文件包含三個(gè)區(qū)域:
- Header:保存Mach-O的一些基本信息,包括平臺、文件類型、指令數(shù)、指令總大小,dyld標(biāo)記Flags等等。
- Load Commands:緊跟Header,加載Mach-O文件時(shí)會使用這部分?jǐn)?shù)據(jù)確定內(nèi)存分布,對系統(tǒng)內(nèi)核加載器和動態(tài)連接器起指導(dǎo)作用。
- Data:每個(gè)segment的具體數(shù)據(jù)保存在這里,包含具體的代碼、數(shù)據(jù)等等。
用一張圖表示Mach-O
?
3、執(zhí)行流程:
說到可執(zhí)行文件肯定離不開進(jìn)程。在?Linux?中,我們會通過?Fork()來新創(chuàng)建子進(jìn)程,然后執(zhí)行鏡像通過exec()來替換為另一個(gè)可執(zhí)行程序。
- 進(jìn)程可以通過fork()系統(tǒng)調(diào)用來創(chuàng)建子進(jìn)程,子進(jìn)程得到與父進(jìn)程地址空間相同的(但是獨(dú)立的)一份拷貝,包括文本、數(shù)據(jù)和bss段、堆以及用戶棧等,但是新線程只會復(fù)制調(diào)用fork的線程。所有父進(jìn)程中別的線程,到了子進(jìn)程中都是突然“蒸發(fā)”掉的。
- 我們在線程問題中經(jīng)常會提到鎖,每個(gè)鎖都有一個(gè)持有者(最后一次lock它的線程)。為了性能,鎖對象會因?yàn)閒ork復(fù)制到子進(jìn)程中,但是子進(jìn)程只復(fù)制調(diào)用fork的線程,很可能并不擁有鎖持有者線程,那么就沒有辦法解開鎖,導(dǎo)致死鎖問題、內(nèi)存泄漏
- 避免死鎖的方法:在子線程中馬上調(diào)用exec函數(shù),一個(gè)進(jìn)程一旦調(diào)用exec類函數(shù),它本身就”死亡”了,系統(tǒng)把代碼段替換成新的程序的代碼,廢棄原有的數(shù)據(jù)段和堆棧段,并為新程序分配新的數(shù)據(jù)段與堆棧段,唯一留下的,就是進(jìn)程號,也就是說,對系統(tǒng)而言,還是同一個(gè)進(jìn)程,不過已經(jīng)是另一個(gè)程序了。
綜上所述,我們在用戶態(tài)會通過exec*系列函數(shù)來加載一個(gè)可執(zhí)行文件,同時(shí)exec*都只是對系統(tǒng)調(diào)用execve的封裝,那我們加載Mach-O的流程,就從execve說起。Mach-O有多種文件類型,比如MH_DYLIB文件、MH_BUNDLE文件、MH_EXECUTE文件(這些需要dyld動態(tài)加載),MH_OBJECT(內(nèi)核加載)等。所以一個(gè)進(jìn)程往往不是只需要內(nèi)核加載器就可以完成加載的,需要dyld來進(jìn)行動態(tài)加載配合。如圖:
- execve這個(gè)函數(shù)只是直接調(diào)用?__mac_execve()。可下載源碼解讀:Source Browser (apple.com)
- __mac_execve():主要是為加載鏡像進(jìn)行數(shù)據(jù)的初始化,以及資源相關(guān)的操作,在其內(nèi)部會執(zhí)行exec_activate_image(),鏡像加載的工作都是由它完成的
- exec_activate_image:主要是用來對Mach-O做檢測,會檢測Mach-O頭部,解析其架構(gòu)、檢查imgp等內(nèi)容,并拒絕接受Dylib和Bundle這樣的文件,這些文件會由dyld負(fù)責(zé)加載。然后把Mach-O映射到內(nèi)存中去,調(diào)用load_machfile()
- load_machfile:load_machfile會加載Mach-O中的各種load monmand命令。在其內(nèi)部會禁止數(shù)據(jù)段執(zhí)行,防止溢出漏洞攻擊,還會設(shè)置地址空間布局隨機(jī)化(ASLR),還有一些映射的調(diào)整。真正負(fù)責(zé)對加載命令解析的是parse_machfile()
- parse_machfile會根據(jù)load_command的種類選擇不同的函數(shù)來加載,內(nèi)部是一個(gè)Switch語句來實(shí)現(xiàn)的。常見的命令有LC_SEGMENT_64、LC_LOAD_DYLINKER、LC_CODE_SIGNATURE、LC_UUID等,更多命令可以查看Mach-O文件格式。對于命令的加載會進(jìn)行多次掃描,當(dāng)掃描三次之后,并且存在dylinker_command命令時(shí),會執(zhí)行?load_dylinker(),啟動動態(tài)鏈接器 (dyld)
- 動態(tài)鏈接也有區(qū)分,一種是加載主程序(很多博客里這么寫),是由load commands指定的dylib,以靜態(tài)的方式存放在二進(jìn)制文件里,一種是由DYLD_INSERT_LIBRARIES動態(tài)指定。下面這種就是提前指定在二進(jìn)制文件中的動態(tài)庫,下面的闡述主要是站在前者的角度,對于動態(tài)指定的后期再研究
- 內(nèi)核會加載dyld并調(diào)用dyld_start方法,隨后dyld_start會調(diào)用_main(),在_main函數(shù)中對數(shù)據(jù)進(jìn)行一通初始化之后,就會調(diào)用instantiateFromLoadedImage函數(shù)初始化ImageLoader實(shí)例
- instantiateFromLoadedImage:這個(gè)函數(shù)內(nèi)的代碼比較容易理解,檢測Mach-O是否合法,合法的話就初始化ImageLoader實(shí)例,然后將其加入到一個(gè)全局的管理ImageLoader的數(shù)組中去isCompatibleMachO會對Mach-O頭部的一些信息與當(dāng)前平臺進(jìn)行比較,判斷其合法性。
- ImageLoader:是一個(gè)抽象基類,每一個(gè)動態(tài)加載的可執(zhí)行文件都會初始化一個(gè)ImageLoader實(shí)例
- instantiateMainExecutable:ImageLoaderMachOCompressed與ImageLoaderMachOClassic均繼承于ImageLoaderMachO,ImageLoaderMachO?繼承于ImageLoader。sniffLoadCommands會對Mach-O是classic還是compressed的做一個(gè)判斷。instantiateMainExecutable是對ImageLoaderMachOCompressed或ImageLoaderMachOClassic做初始化,并加載load comond命令
?
九、GCD源碼解讀之mach
Mach的獨(dú)特之處在于選擇了通過消息傳遞的方式實(shí)現(xiàn)對象與對象之間的通信。而其他架構(gòu)一個(gè)對象要訪問另一個(gè)對象需要通過一個(gè)大家都知道的接口,而Mach對象不能直接調(diào)用另一個(gè)對象,而是必須傳遞消息。
一條消息就像網(wǎng)絡(luò)包一樣,定義為透明的blob(binary larger object,二進(jìn)制大對象),通過固定的包頭進(jìn)行分裝
typedef struct {mach_msg_header_t header;mach_msg_body_t body; } mach_msg_base_t;typedef struct {mach_msg_bits_t msgh_bits; // 消息頭標(biāo)志位mach_msg_size_t msgh_size; // 大小mach_port_t msgh_remote_port; // 目標(biāo)(發(fā)消息)或源(接消息)mach_port_t msgh_local_port; // 源(發(fā)消息)或目標(biāo)(接消息)mach_port_name_t msgh_voucher_port;mach_msg_id_t msgh_id; // 唯一id } mach_msg_header_t;?
Mach消息的發(fā)送和接收都是通過同一個(gè)API函數(shù)mach_msg()進(jìn)行的。這個(gè)函數(shù)在用戶態(tài)和內(nèi)核態(tài)都有實(shí)現(xiàn)。為了實(shí)現(xiàn)消息的發(fā)送和接收,mach_msg()函數(shù)調(diào)用了一個(gè)Mach陷阱(trap)。Mach陷阱就是Mach中和系統(tǒng)調(diào)用等同的概念。在用戶態(tài)調(diào)用mach_msg_trap()會引發(fā)陷阱機(jī)制,切換到內(nèi)核態(tài),在內(nèi)核態(tài)中,內(nèi)核實(shí)現(xiàn)的mach_msg()會完成實(shí)際的工作。這個(gè)函數(shù)也將會在下面的源碼分析中遇到。
每一個(gè)BSD進(jìn)程都在底層關(guān)聯(lián)一個(gè)Mach任務(wù)對象,因?yàn)镸ach提供的都是非常底層的抽象,提供的API從設(shè)計(jì)上講很基礎(chǔ)且不完整,所以需要在這之上提供一個(gè)更高的層次以實(shí)現(xiàn)完整的功能。我們開發(fā)層遇到的進(jìn)程和線程就是BSD層對Mach的任務(wù)和線程的復(fù)雜包裝。
進(jìn)程填充的是線程,而線程是二進(jìn)制代碼的實(shí)際執(zhí)行單元。用戶態(tài)的線程始于對pthread_create的調(diào)用。這個(gè)函數(shù)的又由bsdthread_create系統(tǒng)調(diào)用完成,而bsdthread_create又其實(shí)是Mach中的thread_create的復(fù)雜包裝,說到底真正的線程創(chuàng)建還是有Mach層完成。
在UNIX中,進(jìn)程不能被創(chuàng)建出來,都是通過fork()系統(tǒng)調(diào)用復(fù)制出來的。復(fù)制出來的進(jìn)程都會被要加載的執(zhí)行程序覆蓋整個(gè)內(nèi)存空間。
十、源碼中常見的宏
1、__builtin_expect
這個(gè)其實(shí)是個(gè)函數(shù),針對編譯器優(yōu)化的一個(gè)函數(shù),后面幾個(gè)宏是對這個(gè)函數(shù)的封裝,所以提前拎出來說一下。寫代碼中我們經(jīng)常會遇到條件判斷語句
if(今天是工作日) {printf("好好上班"); }else{printf("好好睡覺"); }CPU讀取指令的時(shí)候并非一條一條的來讀,而是多條一起加載進(jìn)來,比如已經(jīng)加載了if(今天是工作日) printf(“好好上班”);的指令,這時(shí)候條件式如果為非,也就是非工作日,那么CPU繼續(xù)把printf(“好好睡覺”);這條指令加載進(jìn)來,這樣就造成了性能浪費(fèi)的現(xiàn)象。?__builtin_expect的第一個(gè)參數(shù)是實(shí)際值,第二個(gè)參數(shù)是預(yù)測值。使用這個(gè)目的是告訴編譯器if條件式是不是有更大的可能被滿足。
2、 likely和unlikely
解開這個(gè)宏后其實(shí)是對__builtin_expect封裝,likely表示更大可能成立,unlikely表示更大可能不成立。
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)遇到這樣的,if(likely(a == 0))理解成if(a==0)即可,unlikely也是同樣的。
?3、?fastpath和slowpath
跟上面也是差不多的,fastpath表示更大可能成立,slowpath表示更大可能不成立
#define fastpath(x) ((typeof(x))__builtin_expect(_safe_cast_to_long(x), ~0l)) #define slowpath(x) ((typeof(x))__builtin_expect(_safe_cast_to_long(x), 0l))這兩個(gè)理解起來跟likely和unlikely一樣,只需要關(guān)注里面的條件式是否滿足即可。
4、 os_atomic_cmpxchg
其內(nèi)部就是atomic_compare_exchange_strong_explicit函數(shù),這個(gè)函數(shù)的作用是:第二個(gè)參數(shù)與第一個(gè)參數(shù)值比較,如果相等,第三個(gè)參數(shù)的值替換第一個(gè)參數(shù)的值。如果不相等,把第一個(gè)參數(shù)的值賦值到第二個(gè)參數(shù)上。
#define os_atomic_cmpxchg(p, e, v, m) \({ _os_atomic_basetypeof(p) _r = (e); \atomic_compare_exchange_strong_explicit(_os_atomic_c11_atomic(p), \&_r, v, memory_order_##m, memory_order_relaxed); })5、os_atomic_store2o
將第二個(gè)參數(shù),保存到第一個(gè)參數(shù)
#define os_atomic_store2o(p, f, v, m) os_atomic_store(&(p)->f, (v), m) #define os_atomic_store(p, v, m) \atomic_store_explicit(_os_atomic_c11_atomic(p), v, memory_order_##m)6、os_atomic_inc_orig
將1保存到第一個(gè)參數(shù)中
#define os_atomic_inc_orig(p, m) os_atomic_add_orig((p), 1, m) #define os_atomic_add_orig(p, v, m) _os_atomic_c11_op_orig((p), (v), m, add, +) #define _os_atomic_c11_op_orig(p, v, m, o, op) \atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), v, \memory_order_##m)十一、常用數(shù)據(jù)結(jié)構(gòu)體
1、dispatch_queue_t
typedef struct dispatch_queue_s *dispatch_queue_t;我們看下dispatch_queue_s怎么定義的。發(fā)現(xiàn)其內(nèi)部有個(gè)_DISPATCH_QUEUE_HEADER宏定義。struct dispatch_queue_s {_DISPATCH_QUEUE_HEADER(queue);DISPATCH_QUEUE_CACHELINE_PADDING; } DISPATCH_ATOMIC64_ALIGN;解開_DISPATCH_QUEUE_HEADER后發(fā)現(xiàn)又一個(gè)DISPATCH_OBJECT_HEADER宏定義,繼續(xù)拆解#define _DISPATCH_QUEUE_HEADER(x) \struct os_mpsc_queue_s _as_oq[0]; \DISPATCH_OBJECT_HEADER(x); \_OS_MPSC_QUEUE_FIELDS(dq, dq_state); \uint32_t dq_side_suspend_cnt; \dispatch_unfair_lock_s dq_sidelock; \union { \dispatch_queue_t dq_specific_q; \struct dispatch_source_refs_s *ds_refs; \struct dispatch_timer_source_refs_s *ds_timer_refs; \struct dispatch_mach_recv_refs_s *dm_recv_refs; \}; \DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \const uint16_t dq_width, \const uint16_t __dq_opaque \); \DISPATCH_INTROSPECTION_QUEUE_HEADER還有一層宏_DISPATCH_OBJECT_HEADER#define DISPATCH_OBJECT_HEADER(x) \struct dispatch_object_s _as_do[0]; \_DISPATCH_OBJECT_HEADER(x)不熟悉##的作用的同學(xué),這里先說明下這個(gè)作用就拼接成字符串,比如x為group的話,下面就會拼接為dispatch_group這樣的。
#define _DISPATCH_OBJECT_HEADER(x) \struct _os_object_s _as_os_obj[0]; \OS_OBJECT_STRUCT_HEADER(dispatch_##x); \struct dispatch_##x##_s *volatile do_next; \struct dispatch_queue_s *do_targetq; \void *do_ctxt; \void *do_finalizer來到OS_OBJECT_STRUCT_HEADER之后,我們需要注意一個(gè)成員變量,記住這個(gè)成員變量名字叫做do_vtable。再繼續(xù)拆解_OS_OBJECT_HEADER發(fā)現(xiàn)里面起就是一個(gè)isa指針和引用計(jì)數(shù)一些信息。
#define OS_OBJECT_STRUCT_HEADER(x) \_OS_OBJECT_HEADER(\const void *_objc_isa, \do_ref_cnt, \do_xref_cnt); \// 注意這個(gè)成員變量,后面將任務(wù)Push到隊(duì)列就是通過這個(gè)變量const struct x##_vtable_s *do_vtable#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \isa; /* must be pointer-sized */ \int volatile ref_cnt; \int volatile xref_cnt 復(fù)制代碼2、dispatch_continuation_t
說到這個(gè)結(jié)構(gòu)體,如果沒看過源碼的話,肯定對這個(gè)結(jié)構(gòu)體很陌生,因?yàn)閷ν獾腶pi里面沒有跟continuation有關(guān)的。所以這里先說下這個(gè)結(jié)構(gòu)體就是用來封裝block對象的,保存block的上下文環(huán)境和block執(zhí)行函數(shù)等。
typedef struct dispatch_continuation_s {struct dispatch_object_s _as_do[0];DISPATCH_CONTINUATION_HEADER(continuation); } *dispatch_continuation_t;看下里面的宏:DISPATCH_CONTINUATION_HEADER
#define DISPATCH_CONTINUATION_HEADER(x) \union { \const void *do_vtable; \uintptr_t dc_flags; \}; \union { \pthread_priority_t dc_priority; \int dc_cache_cnt; \uintptr_t dc_pad; \}; \struct dispatch_##x##_s *volatile do_next; \struct voucher_s *dc_voucher; \dispatch_function_t dc_func; \void *dc_ctxt; \void *dc_data; \void *dc_other3、dispatch_object_t
typedef union {struct _os_object_s *_os_obj;struct dispatch_object_s *_do;struct dispatch_continuation_s *_dc;struct dispatch_queue_s *_dq;struct dispatch_queue_attr_s *_dqa;struct dispatch_group_s *_dg;struct dispatch_source_s *_ds;struct dispatch_mach_s *_dm;struct dispatch_mach_msg_s *_dmsg;struct dispatch_source_attr_s *_dsa;struct dispatch_semaphore_s *_dsema;struct dispatch_data_s *_ddata;struct dispatch_io_s *_dchannel;struct dispatch_operation_s *_doperation;struct dispatch_disk_s *_ddisk; } dispatch_object_t DISPATCH_TRANSPARENT_UNION;4、dispatch_function_t
dispatch_function_t只是一個(gè)函數(shù)指針
typedef void (*dispatch_function_t)(void *_Nullable);十二、GCD相關(guān)的源碼
1、dispatch_queue_create
先從創(chuàng)建隊(duì)列開始。我們已經(jīng)很熟悉,創(chuàng)建隊(duì)列的方法是調(diào)用dispatch_queue_create函數(shù)。
DISPATCH_TARGET_QUEUE_DEFAULT這個(gè)宏其實(shí)就是nulldispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr){ // attr一般我們都是傳DISPATCH_QUEUE_SERIAL、DISPATCH_QUEUE_CONCURRENT或者nil// 而DISPATCH_QUEUE_SERIAL其實(shí)就是nullreturn _dispatch_queue_create_with_target(label, attr,DISPATCH_TARGET_QUEUE_DEFAULT, true);}其內(nèi)部又調(diào)用了_dispatch_queue_create_with_target函數(shù)
1、_dispatch_queue_create_with_target
創(chuàng)建一個(gè)root隊(duì)列,并將自己新建的隊(duì)列綁定到所對應(yīng)的root隊(duì)列上
static dispatch_queue_t _dispatch_queue_create_with_target(const char *label, dispatch_queue_attr_t dqa,dispatch_queue_t tq, bool legacy) { // 根據(jù)上文代碼注釋里提到的,作者認(rèn)為調(diào)用者傳入DISPATCH_QUEUE_SERIAL和nil的幾率要大于傳DISPATCH_QUEUE_CONCURRENT。所以這里設(shè)置個(gè)默認(rèn)值。// 這里怎么理解呢?只要看做if(!dqa)即可if (!slowpath(dqa)) {// _dispatch_get_default_queue_attr里面會將dqa的dqa_autorelease_frequency指定為DISPATCH_AUTORELEASE_FREQUENCY_INHERIT的,inactive也指定為false。這里就不展開了,只需要知道賦了哪些值。因?yàn)楹竺鏁玫健qa = _dispatch_get_default_queue_attr();} else if (dqa->do_vtable != DISPATCH_VTABLE(queue_attr)) {DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");}// 取出優(yōu)先級dispatch_qos_t qos = _dispatch_priority_qos(dqa->dqa_qos_and_relpri);// overcommit單純從英文理解表示過量使用的意思,那這里這個(gè)overcommit就是一個(gè)標(biāo)識符,表示是不是就算負(fù)荷很高了,但還是得給我新開一個(gè)線程出來給我執(zhí)行任務(wù)。_dispatch_queue_attr_overcommit_t overcommit = dqa->dqa_overcommit;if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {if (tq->do_targetq) {DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and ""a non-global target queue");}}// 如果overcommit沒有被指定if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {// 所以對于overcommit,如果是串行的話默認(rèn)是開啟的,而并行是關(guān)閉的overcommit = dqa->dqa_concurrent ?_dispatch_queue_attr_overcommit_disabled :_dispatch_queue_attr_overcommit_enabled;}// 之前說過初始化隊(duì)列默認(rèn)傳了DISPATCH_TARGET_QUEUE_DEFAULT,也就是null,所以進(jìn)入if語句。if (!tq) {// 獲取一個(gè)管理自己隊(duì)列的root隊(duì)列。tq = _dispatch_get_root_queue(qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,overcommit == _dispatch_queue_attr_overcommit_enabled);if (slowpath(!tq)) {DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");}}// legacy默認(rèn)是true的if (legacy) {// 之前說過,默認(rèn)是會給dqa_autorelease_frequency指定為DISPATCH_AUTORELEASE_FREQUENCY_INHERIT,所以這個(gè)判斷式是成立的if (dqa->dqa_inactive || dqa->dqa_autorelease_frequency) {legacy = false;}}// vtable變量很重要,之后會被賦值到之前說的dispatch_queue_t結(jié)構(gòu)體里的do_vtable變量上const void *vtable;dispatch_queue_flags_t dqf = 0;// legacy變?yōu)閒alse了if (legacy) {vtable = DISPATCH_VTABLE(queue);} else if (dqa->dqa_concurrent) {// 如果創(chuàng)建隊(duì)列的時(shí)候傳了DISPATCH_QUEUE_CONCURRENT,就是走這里vtable = DISPATCH_VTABLE(queue_concurrent);} else {// 如果創(chuàng)建線程沒有指定為并行隊(duì)列,無論你傳DISPATCH_QUEUE_SERIAL還是nil,都會創(chuàng)建一個(gè)串行隊(duì)列。vtable = DISPATCH_VTABLE(queue_serial);}if (label) {// 判斷傳進(jìn)來的字符串是否可變的,如果可變的copy成一份不可變的const char *tmp = _dispatch_strdup_if_mutable(label);if (tmp != label) {dqf |= DQF_LABEL_NEEDS_FREE;label = tmp;}}// _dispatch_object_alloc里面就將vtable賦值給do_vtable變量上了。dispatch_queue_t dq = _dispatch_object_alloc(vtable,sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_CACHELINE_PAD);// 第三個(gè)參數(shù)根據(jù)是否并行隊(duì)列,如果不是則最多開一個(gè)線程,如果是則最多開0x1000 - 2個(gè)線程,這個(gè)數(shù)量很驚人了已經(jīng),換成十進(jìn)制就是(4096 - 2)個(gè)。// dqa_inactive之前說串行是false的// DISPATCH_QUEUE_ROLE_INNER 也是0,所以這里串行隊(duì)列的話dqa->dqa_state是0_dispatch_queue_init(dq, dqf, dqa->dqa_concurrent ?DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |(dqa->dqa_inactive ? DISPATCH_QUEUE_INACTIVE : 0));dq->dq_label = label; #if HAVE_PTHREAD_WORKQUEUE_QOSdq->dq_priority = dqa->dqa_qos_and_relpri;if (overcommit == _dispatch_queue_attr_overcommit_enabled) {dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;} #endif_dispatch_retain(tq);if (qos == QOS_CLASS_UNSPECIFIED) {_dispatch_queue_priority_inherit_from_target(dq, tq);}if (!dqa->dqa_inactive) {_dispatch_queue_inherit_wlh_from_target(dq, tq);}// 自定義的queue的目標(biāo)隊(duì)列是root隊(duì)列dq->do_targetq = tq;_dispatch_object_debug(dq, "%s", __func__);return _dispatch_introspection_queue_create(dq); }這個(gè)函數(shù)里面還是有幾個(gè)重要的地方拆出來看下,首先是創(chuàng)建一個(gè)root隊(duì)列_dispatch_get_root_queue函數(shù)。
2、_dispatch_get_root_queue函數(shù)
取root隊(duì)列,一般是從一個(gè)裝有12個(gè)root隊(duì)列數(shù)組里面取。
static inline dispatch_queue_t _dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit) {if (unlikely(qos == DISPATCH_QOS_UNSPECIFIED || qos > DISPATCH_QOS_MAX)) {DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");}return &_dispatch_root_queues[2 * (qos - 1) + overcommit]; }3、_dispatch_root_queues數(shù)組
struct dispatch_queue_s _dispatch_root_queues[] = { #define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \DISPATCH_ROOT_QUEUE_IDX_##n##_QOS) #define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \[_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \DISPATCH_GLOBAL_OBJECT_HEADER(queue_root), \.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \.do_ctxt = &_dispatch_root_queue_contexts[ \_DISPATCH_ROOT_QUEUE_IDX(n, flags)], \.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \.dq_priority = _dispatch_priority_make(DISPATCH_QOS_##n, 0) | flags | \DISPATCH_PRIORITY_FLAG_ROOTQUEUE | \((flags & DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE) ? 0 : \DISPATCH_QOS_##n << DISPATCH_PRIORITY_OVERRIDE_SHIFT), \__VA_ARGS__ \}_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,.dq_label = "com.apple.root.maintenance-qos",.dq_serialnum = 4,),_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.maintenance-qos.overcommit",.dq_serialnum = 5,),_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,.dq_label = "com.apple.root.background-qos",.dq_serialnum = 6,),_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.background-qos.overcommit",.dq_serialnum = 7,),_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,.dq_label = "com.apple.root.utility-qos",.dq_serialnum = 8,),_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.utility-qos.overcommit",.dq_serialnum = 9,),_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE,.dq_label = "com.apple.root.default-qos",.dq_serialnum = 10,),_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.default-qos.overcommit",.dq_serialnum = 11,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,.dq_label = "com.apple.root.user-initiated-qos",.dq_serialnum = 12,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.user-initiated-qos.overcommit",.dq_serialnum = 13,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,.dq_label = "com.apple.root.user-interactive-qos",.dq_serialnum = 14,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.user-interactive-qos.overcommit",.dq_serialnum = 15,), };我們可以看到,每一個(gè)優(yōu)先級都有對應(yīng)的root隊(duì)列,每一個(gè)優(yōu)先級又分為是不是可以過載的隊(duì)列。
struct dispatch_queue_s _dispatch_root_queues[] = { #define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \DISPATCH_ROOT_QUEUE_IDX_##n##_QOS) #define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \[_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \DISPATCH_GLOBAL_OBJECT_HEADER(queue_root), \.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \.do_ctxt = &_dispatch_root_queue_contexts[ \_DISPATCH_ROOT_QUEUE_IDX(n, flags)], \.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \.dq_priority = _dispatch_priority_make(DISPATCH_QOS_##n, 0) | flags | \DISPATCH_PRIORITY_FLAG_ROOTQUEUE | \((flags & DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE) ? 0 : \DISPATCH_QOS_##n << DISPATCH_PRIORITY_OVERRIDE_SHIFT), \__VA_ARGS__ \}_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,.dq_label = "com.apple.root.maintenance-qos",.dq_serialnum = 4,),_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.maintenance-qos.overcommit",.dq_serialnum = 5,),_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,.dq_label = "com.apple.root.background-qos",.dq_serialnum = 6,),_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.background-qos.overcommit",.dq_serialnum = 7,),_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,.dq_label = "com.apple.root.utility-qos",.dq_serialnum = 8,),_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.utility-qos.overcommit",.dq_serialnum = 9,),_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE,.dq_label = "com.apple.root.default-qos",.dq_serialnum = 10,),_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.default-qos.overcommit",.dq_serialnum = 11,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,.dq_label = "com.apple.root.user-initiated-qos",.dq_serialnum = 12,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.user-initiated-qos.overcommit",.dq_serialnum = 13,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,.dq_label = "com.apple.root.user-interactive-qos",.dq_serialnum = 14,),_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,.dq_label = "com.apple.root.user-interactive-qos.overcommit",.dq_serialnum = 15,), };4、DISPATCH_GLOBAL_OBJECT_HEADER
DISPATCH_GLOBAL_OBJECT_HEADER(queue_root),解析到最后是OSdispatch##name##_class這樣的這樣的,對應(yīng)的實(shí)例對象是如下代碼,指定了root隊(duì)列各個(gè)操作對應(yīng)的函數(shù)。
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_root, queue,.do_type = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,.do_kind = "global-queue",.do_dispose = _dispatch_pthread_root_queue_dispose,.do_push = _dispatch_root_queue_push,.do_invoke = NULL,.do_wakeup = _dispatch_root_queue_wakeup,.do_debug = dispatch_queue_debug, );其次看下DISPATCH_VTABLE這個(gè)宏,這個(gè)宏很重要。最后解封也是&OSdispatch##name##_class這樣的。其實(shí)就是取dispatch_object_t對象。 ??如下代碼,這里再舉個(gè)VTABLE的串行對象,里面有各個(gè)狀態(tài)該執(zhí)行的函數(shù):銷毀函、掛起、恢復(fù)、push等函數(shù)都是在這里指定的。所以這里的do_push我們需要特別留意,后面push block任務(wù)到隊(duì)列,就是通過調(diào)用do_push。?
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, queue,.do_type = DISPATCH_QUEUE_SERIAL_TYPE,.do_kind = "serial-queue",.do_dispose = _dispatch_queue_dispose,.do_suspend = _dispatch_queue_suspend,.do_resume = _dispatch_queue_resume,.do_finalize_activation = _dispatch_queue_finalize_activation,.do_push = _dispatch_queue_push,.do_invoke = _dispatch_queue_invoke,.do_wakeup = _dispatch_queue_wakeup,.do_debug = dispatch_queue_debug,.do_set_targetq = _dispatch_queue_set_target_queue, );繼續(xù)看下_dispatch_object_alloc和_dispatch_queue_init兩個(gè)函數(shù),首先看下_dispatch_object_alloc函數(shù)void * _dispatch_object_alloc(const void *vtable, size_t size) { // OS_OBJECT_HAVE_OBJC1為1的滿足式是: // #if TARGET_OS_MAC && !TARGET_OS_SIMULATOR && defined(__i386__) // 所以對于iOS并不滿足 #if OS_OBJECT_HAVE_OBJC1const struct dispatch_object_vtable_s *_vtable = vtable;dispatch_object_t dou;dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);dou._do->do_vtable = vtable;return dou._do; #elsereturn _os_object_alloc_realized(vtable, size); #endif }inline _os_object_t _os_object_alloc_realized(const void *cls, size_t size) {_os_object_t obj;dispatch_assert(size >= sizeof(struct _os_object_s));while (!fastpath(obj = calloc(1u, size))) {_dispatch_temporary_resource_shortage();}obj->os_obj_isa = cls;return obj; }void _dispatch_temporary_resource_shortage(void) {sleep(1);asm(""); // prevent tailcall }再看下_dispatch_queue_init函數(shù),這里也就是做些初始化工作了static inline void _dispatch_queue_init(dispatch_queue_t dq, dispatch_queue_flags_t dqf,uint16_t width, uint64_t initial_state_bits) {uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |DISPATCH_QUEUE_INACTIVE)) == 0);if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {dq_state |= DISPATCH_QUEUE_INACTIVE + DISPATCH_QUEUE_NEEDS_ACTIVATION;dq_state |= DLOCK_OWNER_MASK;dq->do_ref_cnt += 2; }dq_state |= (initial_state_bits & DISPATCH_QUEUE_ROLE_MASK);// 指向DISPATCH_OBJECT_LISTLESS是優(yōu)化編譯器的作用。只是為了生成更好的指令讓CPU更好的編碼dq->do_next = (struct dispatch_queue_s *)DISPATCH_OBJECT_LISTLESS;dqf |= DQF_WIDTH(width);// dqf 保存進(jìn) dq->dq_atomic_flagsos_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);dq->dq_state = dq_state;dq->dq_serialnum =os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed); }最后是_dispatch_introspection_queue_create函數(shù),一個(gè)內(nèi)省函數(shù)。dispatch_queue_t _dispatch_introspection_queue_create(dispatch_queue_t dq) {TAILQ_INIT(&dq->diq_order_top_head);TAILQ_INIT(&dq->diq_order_bottom_head);_dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);TAILQ_INSERT_TAIL(&_dispatch_introspection.queues, dq, diq_list);_dispatch_unfair_lock_unlock(&_dispatch_introspection.queues_lock);DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {_dispatch_introspection_queue_create_hook(dq);}return dq; }至此,一個(gè)隊(duì)列的創(chuàng)建過程我們大致了解了。
5、隊(duì)列的創(chuàng)建過程大致可以分為以下幾點(diǎn)
- ? ? 設(shè)置隊(duì)列優(yōu)先級
- ? ? 默認(rèn)創(chuàng)建的是一個(gè)串行隊(duì)列
- ? ? 設(shè)置隊(duì)列掛載的根隊(duì)列。優(yōu)先級不同根隊(duì)列也不同
- ? ? 實(shí)例化vtable對象,這個(gè)對象給不同隊(duì)列指定了push、wakeup等函數(shù)。
2、dispatch_sync
dispatch_sync直接調(diào)用的是dispatch_sync_f
void dispatch_sync(dispatch_queue_t dq, dispatch_block_t work) {// 很大可能不會走if分支,看做if(_dispatch_block_has_private_data(work))if (unlikely(_dispatch_block_has_private_data(work))) {return _dispatch_sync_block_with_private_data(dq, work, 0);}dispatch_sync_f(dq, work, _dispatch_Block_invoke(work)); }void dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) {// 串行隊(duì)列會走到這個(gè)if分支if (likely(dq->dq_width == 1)) {return dispatch_barrier_sync_f(dq, ctxt, func);}// 全局獲取的并行隊(duì)列或者綁定的是非調(diào)度線程的隊(duì)列會走進(jìn)這個(gè)if分支if (unlikely(!_dispatch_queue_try_reserve_sync_width(dq))) {return _dispatch_sync_f_slow(dq, ctxt, func, 0);}_dispatch_introspection_sync_begin(dq);if (unlikely(dq->do_targetq->do_targetq)) {return _dispatch_sync_recurse(dq, ctxt, func, 0);}// 自定義并行隊(duì)列會來到這個(gè)函數(shù)_dispatch_sync_invoke_and_complete(dq, ctxt, func); }1、串行隊(duì)列
void dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,dispatch_function_t func) {dispatch_tid tid = _dispatch_tid_self();// 隊(duì)列綁定的是非調(diào)度線程就會走這里if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dq, tid))) {return _dispatch_sync_f_slow(dq, ctxt, func, DISPATCH_OBJ_BARRIER_BIT);}_dispatch_introspection_sync_begin(dq);if (unlikely(dq->do_targetq->do_targetq)) {return _dispatch_sync_recurse(dq, ctxt, func, DISPATCH_OBJ_BARRIER_BIT);}// 一般會走到這里_dispatch_queue_barrier_sync_invoke_and_complete(dq, ctxt, func); }static void _dispatch_queue_barrier_sync_invoke_and_complete(dispatch_queue_t dq,void *ctxt, dispatch_function_t func) {// 首先會執(zhí)行這個(gè)函數(shù)_dispatch_sync_function_invoke_inline(dq, ctxt, func);// 如果后面還有別的任務(wù)if (unlikely(dq->dq_items_tail || dq->dq_width > 1)) {// 內(nèi)部其實(shí)就是喚醒隊(duì)列return _dispatch_queue_barrier_complete(dq, 0, 0);}const uint64_t fail_unlock_mask = DISPATCH_QUEUE_SUSPEND_BITS_MASK |DISPATCH_QUEUE_ENQUEUED | DISPATCH_QUEUE_DIRTY |DISPATCH_QUEUE_RECEIVED_OVERRIDE | DISPATCH_QUEUE_SYNC_TRANSFER |DISPATCH_QUEUE_RECEIVED_SYNC_WAIT;uint64_t old_state, new_state;// 原子鎖。檢查dq->dq_state與old_state是否相等,如果相等把new_state賦值給dq->dq_state,如果不相等,把dq_state賦值給old_state。// 串行隊(duì)列走到這里,dq->dq_state與old_state是相等的,會把new_state也就是閉包里的賦值的值給dq->dq_stateos_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, release, {new_state = old_state - DISPATCH_QUEUE_SERIAL_DRAIN_OWNED;new_state &= ~DISPATCH_QUEUE_DRAIN_UNLOCK_MASK;new_state &= ~DISPATCH_QUEUE_MAX_QOS_MASK;if (unlikely(old_state & fail_unlock_mask)) {os_atomic_rmw_loop_give_up({return _dispatch_queue_barrier_complete(dq, 0, 0);});}});if (_dq_state_is_base_wlh(old_state)) {_dispatch_event_loop_assert_not_owned((dispatch_wlh_t)dq);} }static inline void _dispatch_sync_function_invoke_inline(dispatch_queue_t dq, void *ctxt,dispatch_function_t func) {// 保護(hù)現(xiàn)場 -> 調(diào)用函數(shù) -> 恢復(fù)現(xiàn)場dispatch_thread_frame_s dtf;_dispatch_thread_frame_push(&dtf, dq);_dispatch_client_callout(ctxt, func);_dispatch_perfmon_workitem_inc();_dispatch_thread_frame_pop(&dtf); }然后另一種情況,自定義并行隊(duì)列會走_(dá)dispatch_sync_invoke_and_complete函數(shù)。static void _dispatch_sync_invoke_and_complete(dispatch_queue_t dq, void *ctxt,dispatch_function_t func) {_dispatch_sync_function_invoke_inline(dq, ctxt, func);// 將自定義隊(duì)列加入到root隊(duì)列里去// dispatch_async也會調(diào)用此方法,之前我們初始化的時(shí)候會綁定一個(gè)root隊(duì)列,這里就將我們新建的隊(duì)列交給root隊(duì)列進(jìn)行管理_dispatch_queue_non_barrier_complete(dq); }static inline void _dispatch_sync_function_invoke_inline(dispatch_queue_t dq, void *ctxt,dispatch_function_t func) {dispatch_thread_frame_s dtf;_dispatch_thread_frame_push(&dtf, dq);// 執(zhí)行任務(wù)_dispatch_client_callout(ctxt, func);_dispatch_perfmon_workitem_inc();_dispatch_thread_frame_pop(&dtf); }## dispatch_async內(nèi)部就是兩個(gè)函數(shù)_dispatch_continuation_init和_dispatch_continuation_asyncvoid dispatch_async(dispatch_queue_t dq, dispatch_block_t work) {dispatch_continuation_t dc = _dispatch_continuation_alloc();// 設(shè)置標(biāo)識位uintptr_t dc_flags = DISPATCH_OBJ_CONSUME_BIT;_dispatch_continuation_init(dc, dq, work, 0, 0, dc_flags);_dispatch_continuation_async(dq, dc); }_dispatch_continuation_init函數(shù)只是一個(gè)初始化,主要就是保存Block上下文,指定block的執(zhí)行函數(shù)static inline void _dispatch_continuation_init(dispatch_continuation_t dc,dispatch_queue_class_t dqu, dispatch_block_t work,pthread_priority_t pp, dispatch_block_flags_t flags, uintptr_t dc_flags) {dc->dc_flags = dc_flags | DISPATCH_OBJ_BLOCK_BIT;// block對象賦值到dc_ctxtdc->dc_ctxt = _dispatch_Block_copy(work);// 設(shè)置默認(rèn)任務(wù)優(yōu)先級_dispatch_continuation_priority_set(dc, pp, flags);// 大多數(shù)情況不會走這個(gè)分支if (unlikely(_dispatch_block_has_private_data(work))) {return _dispatch_continuation_init_slow(dc, dqu, flags);}// 這個(gè)標(biāo)識位多眼熟,就是前面入口賦值的,沒的跑了,指定執(zhí)行函數(shù)就是_dispatch_call_block_and_release了if (dc_flags & DISPATCH_OBJ_CONSUME_BIT) {dc->dc_func = _dispatch_call_block_and_release;} else {dc->dc_func = _dispatch_Block_invoke(work);}_dispatch_continuation_voucher_set(dc, dqu, flags); }_dispatch_call_block_and_release這個(gè)函數(shù)就是直接執(zhí)行block了,所以dc->dc_func被調(diào)用的話就block會被直接執(zhí)行了。void _dispatch_call_block_and_release(void *block) {void (^b)(void) = block;b();Block_release(b); }上面的初始化過程就是這樣,接著看下_dispatch_continuation_async函數(shù)void _dispatch_continuation_async(dispatch_queue_t dq, dispatch_continuation_t dc) {// 看看是不是barrier類型的block_dispatch_continuation_async2(dq, dc,dc->dc_flags & DISPATCH_OBJ_BARRIER_BIT); }static inline void _dispatch_continuation_async2(dispatch_queue_t dq, dispatch_continuation_t dc,bool barrier) {// 如果是用barrier插進(jìn)來的任務(wù)或者是串行隊(duì)列,直接將任務(wù)加入到隊(duì)列// #define DISPATCH_QUEUE_USES_REDIRECTION(width) \// ({ uint16_t _width = (width); \// _width > 1 && _width < DISPATCH_QUEUE_WIDTH_POOL; }) if (fastpath(barrier || !DISPATCH_QUEUE_USES_REDIRECTION(dq->dq_width))) {return _dispatch_continuation_push(dq, dc);}return _dispatch_async_f2(dq, dc); }// 可以先看下如果是barrier任務(wù),直接調(diào)用_dispatch_continuation_push函數(shù) static void _dispatch_continuation_push(dispatch_queue_t dq, dispatch_continuation_t dc) {dx_push(dq, dc, _dispatch_continuation_override_qos(dq, dc)); }// _dispatch_continuation_async2函數(shù)里面調(diào)用_dispatch_async_f2函數(shù) static void _dispatch_async_f2(dispatch_queue_t dq, dispatch_continuation_t dc) {// 如果還有任務(wù),slowpath表示很大可能隊(duì)尾是沒有任務(wù)的。// 實(shí)際開發(fā)中也的確如此,一般情況下我們不會dispatch_async之后又馬上跟著一個(gè)dispatch_asyncif (slowpath(dq->dq_items_tail)) {return _dispatch_continuation_push(dq, dc);}if (slowpath(!_dispatch_queue_try_acquire_async(dq))) {return _dispatch_continuation_push(dq, dc);}// 一般會直接來到這里,_dispatch_continuation_override_qos函數(shù)里面主要做的是判斷dq有沒有設(shè)置的優(yōu)先級,如果沒有就用block對象的優(yōu)先級,如果有就用自己的return _dispatch_async_f_redirect(dq, dc,_dispatch_continuation_override_qos(dq, dc)); }static void _dispatch_async_f_redirect(dispatch_queue_t dq,dispatch_object_t dou, dispatch_qos_t qos) {// 這里會走進(jìn)if的語句,因?yàn)開dispatch_object_is_redirection內(nèi)部的dx_type(dou._do) == type條件為否if (!slowpath(_dispatch_object_is_redirection(dou))) {dou._dc = _dispatch_async_redirect_wrap(dq, dou);}// dq換成所綁定的root隊(duì)列dq = dq->do_targetq;// 基本不會走里面的循環(huán),主要做的就是找到根root隊(duì)列while (slowpath(DISPATCH_QUEUE_USES_REDIRECTION(dq->dq_width))) {if (!fastpath(_dispatch_queue_try_acquire_async(dq))) {break;}if (!dou._dc->dc_ctxt) {dou._dc->dc_ctxt = (void *)(uintptr_t)_dispatch_queue_autorelease_frequency(dq);}dq = dq->do_targetq;}// 把裝有block信息的結(jié)構(gòu)體裝進(jìn)所在隊(duì)列對應(yīng)的root_queue里面dx_push(dq, dou, qos); }// dx_push是個(gè)宏定義,這里做的就是將任務(wù)push到任務(wù)隊(duì)列,我們看到這里,就知道dx_push就是調(diào)用對象的do_push。 #define dx_push(x, y, z) dx_vtable(x)->do_push(x, y, z) #define dx_vtable(x) (&(x)->do_vtable->_os_obj_vtable)_dispatch_async_f_redirect函數(shù)里先看這句dou._dc = _dispatch_async_redirect_wrap(dq, dou);static inline dispatch_continuation_t _dispatch_async_redirect_wrap(dispatch_queue_t dq, dispatch_object_t dou) {dispatch_continuation_t dc = _dispatch_continuation_alloc();dou._do->do_next = NULL;// 所以dispatch_async推進(jìn)的任務(wù)的do_vtable成員變量是有值的dc->do_vtable = DC_VTABLE(ASYNC_REDIRECT);dc->dc_func = NULL;dc->dc_ctxt = (void *)(uintptr_t)_dispatch_queue_autorelease_frequency(dq);// 所屬隊(duì)列被裝進(jìn)dou._dc->dc_data里面了dc->dc_data = dq;dc->dc_other = dou._do;dc->dc_voucher = DISPATCH_NO_VOUCHER;dc->dc_priority = DISPATCH_NO_PRIORITY;_dispatch_retain(dq); // released in _dispatch_async_redirect_invokereturn dc; }// dc->do_vtable = DC_VTABLE(ASYNC_REDIRECT); 就是下面指定redirect的invoke函數(shù)是_dispatch_async_redirect_invoke,后面任務(wù)被執(zhí)行就是通過這個(gè)函數(shù) const struct dispatch_continuation_vtable_s _dispatch_continuation_vtables[] = {DC_VTABLE_ENTRY(ASYNC_REDIRECT,.do_kind = "dc-redirect",.do_invoke = _dispatch_async_redirect_invoke), #if HAVE_MACHDC_VTABLE_ENTRY(MACH_SEND_BARRRIER_DRAIN,.do_kind = "dc-mach-send-drain",.do_invoke = _dispatch_mach_send_barrier_drain_invoke),DC_VTABLE_ENTRY(MACH_SEND_BARRIER,.do_kind = "dc-mach-send-barrier",.do_invoke = _dispatch_mach_barrier_invoke),DC_VTABLE_ENTRY(MACH_RECV_BARRIER,.do_kind = "dc-mach-recv-barrier",.do_invoke = _dispatch_mach_barrier_invoke),DC_VTABLE_ENTRY(MACH_ASYNC_REPLY,.do_kind = "dc-mach-async-reply",.do_invoke = _dispatch_mach_msg_async_reply_invoke), #endif #if HAVE_PTHREAD_WORKQUEUE_QOSDC_VTABLE_ENTRY(OVERRIDE_STEALING,.do_kind = "dc-override-stealing",.do_invoke = _dispatch_queue_override_invoke),// 留意這個(gè),后面也會被用到DC_VTABLE_ENTRY(OVERRIDE_OWNING,.do_kind = "dc-override-owning",.do_invoke = _dispatch_queue_override_invoke), #endif };再看dx_push(dq, dou, qos);這句,其實(shí)就是調(diào)用_dispatch_root_queue_push函數(shù)void _dispatch_root_queue_push(dispatch_queue_t rq, dispatch_object_t dou,dispatch_qos_t qos) {// 一般情況下,無論自定義還是非自定義都會走進(jìn)這個(gè)條件式(比如:dispatch_get_global_queue)// 里面主要對比的是qos與root隊(duì)列的qos是否一致。基本上都不一致的,如果不一致走進(jìn)這個(gè)if語句if (_dispatch_root_queue_push_needs_override(rq, qos)) {return _dispatch_root_queue_push_override(rq, dou, qos);}_dispatch_root_queue_push_inline(rq, dou, dou, 1); }static void _dispatch_root_queue_push_override(dispatch_queue_t orig_rq,dispatch_object_t dou, dispatch_qos_t qos) {bool overcommit = orig_rq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT;dispatch_queue_t rq = _dispatch_get_root_queue(qos, overcommit);dispatch_continuation_t dc = dou._dc;// 這個(gè)_dispatch_object_is_redirection函數(shù)其實(shí)就是return _dispatch_object_has_type(dou,DISPATCH_CONTINUATION_TYPE(ASYNC_REDIRECT));// 所以自定義隊(duì)列會走這個(gè)if語句,如果是dispatch_get_global_queue不會走if語句if (_dispatch_object_is_redirection(dc)) {dc->dc_func = (void *)orig_rq;} else {// dispatch_get_global_queue來到這里dc = _dispatch_continuation_alloc();// 相當(dāng)于是下面的,也就是指定了執(zhí)行函數(shù)為_dispatch_queue_override_invoke,所以有別于自定義隊(duì)列的invoke函數(shù)。// DC_VTABLE_ENTRY(OVERRIDE_OWNING,// .do_kind = "dc-override-owning",// .do_invoke = _dispatch_queue_override_invoke),dc->do_vtable = DC_VTABLE(OVERRIDE_OWNING);_dispatch_trace_continuation_push(orig_rq, dou);dc->dc_ctxt = dc;dc->dc_other = orig_rq;dc->dc_data = dou._do;dc->dc_priority = DISPATCH_NO_PRIORITY;dc->dc_voucher = DISPATCH_NO_VOUCHER;}_dispatch_root_queue_push_inline(rq, dc, dc, 1); }static inline void _dispatch_root_queue_push_inline(dispatch_queue_t dq, dispatch_object_t _head,dispatch_object_t _tail, int n) {struct dispatch_object_s *head = _head._do, *tail = _tail._do;// 把任務(wù)裝進(jìn)隊(duì)列,大多數(shù)不走進(jìn)if語句。但是第一個(gè)任務(wù)進(jìn)來之前還是滿足這個(gè)條件式的,會進(jìn)入這個(gè)條件語句去激活隊(duì)列來執(zhí)行里面的任務(wù),后面再加入的任務(wù)因?yàn)殛?duì)列被激活了,所以也就不太需要再進(jìn)入這個(gè)隊(duì)列了,所以相對來說激活隊(duì)列只要一次,所以作者認(rèn)為大多數(shù)情況下不需要走進(jìn)這個(gè)條件語句if (unlikely(_dispatch_queue_push_update_tail_list(dq, head, tail))) {// 保存隊(duì)列頭_dispatch_queue_push_update_head(dq, head);return _dispatch_global_queue_poke(dq, n, 0);} }我們可以看到,我們裝入到自定義的任務(wù)都被扔到其掛靠的root隊(duì)列里去了,所以我們我們自己創(chuàng)建的隊(duì)列只是一個(gè)代理人身份,真正的管理人是其對應(yīng)的root隊(duì)列,但同時(shí)這個(gè)隊(duì)列也是被管理的
2、_dispatch_global_queue_poke函數(shù)
void _dispatch_global_queue_poke(dispatch_queue_t dq, int n, int floor) {return _dispatch_global_queue_poke_slow(dq, n, floor); }繼續(xù)看_dispatch_global_queue_poke函數(shù)調(diào)用了_dispatch_global_queue_poke_slow函數(shù),這里也很關(guān)鍵了,里面執(zhí)行_pthread_workqueue_addthreads函數(shù),把任務(wù)交給內(nèi)核分發(fā)處理_dispatch_global_queue_poke_slow(dispatch_queue_t dq, int n, int floor) {dispatch_root_queue_context_t qc = dq->do_ctxt;int remaining = n;int r = ENOSYS;_dispatch_root_queues_init();_dispatch_debug_root_queue(dq, __func__);if (qc->dgq_kworkqueue != (void*)(~0ul)){r = _pthread_workqueue_addthreads(remaining,_dispatch_priority_to_pp(dq->dq_priority));(void)dispatch_assume_zero(r);return;} }int _pthread_workqueue_addthreads(int numthreads, pthread_priority_t priority) {int res = 0;if (__libdispatch_workerfunction == NULL) {return EPERM;}if ((__pthread_supported_features & PTHREAD_FEATURE_FINEPRIO) == 0) {return ENOTSUP;}res = __workq_kernreturn(WQOPS_QUEUE_REQTHREADS, NULL, numthreads, (int)priority);if (res == -1) {res = errno;}return res; }那么,加入到根隊(duì)列的任務(wù)是怎么被運(yùn)行起來的?在此之前,我們先模擬一下在GCD內(nèi)部把程序搞掛掉,這樣我們就可以追溯下調(diào)用棧關(guān)系。
(0 CoreFoundation 0x00000001093fe12b __exceptionPreprocess + 1711 libobjc.A.dylib 0x0000000108a92f41 objc_exception_throw + 482 CoreFoundation 0x000000010943e0cc _CFThrowFormattedException + 1943 CoreFoundation 0x000000010930c23d -[__NSPlaceholderArray initWithObjects:count:] + 2374 CoreFoundation 0x0000000109312e34 +[NSArray arrayWithObjects:count:] + 525 HotPatch 0x000000010769df77 __29-[ViewController viewDidLoad]_block_invoke + 876 libdispatch.dylib 0x000000010c0a62f7 _dispatch_call_block_and_release + 127 libdispatch.dylib 0x000000010c0a733d _dispatch_client_callout + 88 libdispatch.dylib 0x000000010c0ad754 _dispatch_continuation_pop + 9679 libdispatch.dylib 0x000000010c0abb85 _dispatch_async_redirect_invoke + 78010 libdispatch.dylib 0x000000010c0b3102 _dispatch_root_queue_drain + 77211 libdispatch.dylib 0x000000010c0b2da0 _dispatch_worker_thread3 + 13212 libsystem_pthread.dylib 0x000000010c5f95a2 _pthread_wqthread + 129913 libsystem_pthread.dylib 0x000000010c5f907d start_wqthread + 13 )很明顯,我們已經(jīng)看到加入到隊(duì)列的任務(wù)的調(diào)用關(guān)系是:
start_wqthread -> _pthread_wqthread -> _dispatch_worker_thread3 -> _dispatch_root_queue_drain -> _dispatch_async_redirect_invoke -> _dispatch_continuation_pop -> _dispatch_client_callout -> _dispatch_call_block_and_release3、加入隊(duì)列代碼:
// 根據(jù)優(yōu)先級取出相應(yīng)的root隊(duì)列,再調(diào)用_dispatch_worker_thread4函數(shù) static void _dispatch_worker_thread3(pthread_priority_t pp) {bool overcommit = pp & _PTHREAD_PRIORITY_OVERCOMMIT_FLAG;dispatch_queue_t dq;pp &= _PTHREAD_PRIORITY_OVERCOMMIT_FLAG | ~_PTHREAD_PRIORITY_FLAGS_MASK;_dispatch_thread_setspecific(dispatch_priority_key, (void *)(uintptr_t)pp);dq = _dispatch_get_root_queue(_dispatch_qos_from_pp(pp), overcommit);return _dispatch_worker_thread4(dq); } // 開始調(diào)用_dispatch_root_queue_drain函數(shù),取出任務(wù) static void _dispatch_worker_thread4(void *context) {dispatch_queue_t dq = context;dispatch_root_queue_context_t qc = dq->do_ctxt;_dispatch_introspection_thread_add();int pending = os_atomic_dec2o(qc, dgq_pending, relaxed);dispatch_assert(pending >= 0);_dispatch_root_queue_drain(dq, _dispatch_get_priority());_dispatch_voucher_debug("root queue clear", NULL);_dispatch_reset_voucher(NULL, DISPATCH_THREAD_PARK); } // 循環(huán)取出任務(wù)static void _dispatch_root_queue_drain(dispatch_queue_t dq, pthread_priority_t pp) {_dispatch_queue_set_current(dq);dispatch_priority_t pri = dq->dq_priority;if (!pri) pri = _dispatch_priority_from_pp(pp);dispatch_priority_t old_dbp = _dispatch_set_basepri(pri);_dispatch_adopt_wlh_anon();struct dispatch_object_s *item;bool reset = false;dispatch_invoke_context_s dic = { };dispatch_invoke_flags_t flags = DISPATCH_INVOKE_WORKER_DRAIN |DISPATCH_INVOKE_REDIRECTING_DRAIN;_dispatch_queue_drain_init_narrowing_check_deadline(&dic, pri);_dispatch_perfmon_start();while ((item = fastpath(_dispatch_root_queue_drain_one(dq)))) {if (reset) _dispatch_wqthread_override_reset();_dispatch_continuation_pop_inline(item, &dic, flags, dq);reset = _dispatch_reset_basepri_override();if (unlikely(_dispatch_queue_drain_should_narrow(&dic))) {break;}}// overcommit or not. worker threadif (pri & _PTHREAD_PRIORITY_OVERCOMMIT_FLAG) {_dispatch_perfmon_end(perfmon_thread_worker_oc);} else {_dispatch_perfmon_end(perfmon_thread_worker_non_oc);}_dispatch_reset_wlh();_dispatch_reset_basepri(old_dbp);_dispatch_reset_basepri_override();_dispatch_queue_set_current(NULL); }// 這個(gè)函數(shù)的作用就是調(diào)度出任務(wù)的執(zhí)行函數(shù) static inline void _dispatch_continuation_pop_inline(dispatch_object_t dou,dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags,dispatch_queue_t dq) {dispatch_pthread_root_queue_observer_hooks_t observer_hooks =_dispatch_get_pthread_root_queue_observer_hooks();if (observer_hooks) observer_hooks->queue_will_execute(dq);_dispatch_trace_continuation_pop(dq, dou);flags &= _DISPATCH_INVOKE_PROPAGATE_MASK;// 之前說過dispatch_async是有do_vtable成員變量的,所以會走進(jìn)這個(gè)if分支,又invoke方法指定為_dispatch_async_redirect_invoke,所以執(zhí)行該函數(shù)// 相同的,如果是dispatch_get_global_queue也會走這個(gè)分支,執(zhí)行_dispatch_queue_override_invoke方法,這個(gè)之前也說過了if (_dispatch_object_has_vtable(dou)) {dx_invoke(dou._do, dic, flags);} else {_dispatch_continuation_invoke_inline(dou, DISPATCH_NO_VOUCHER, flags);}if (observer_hooks) observer_hooks->queue_did_execute(dq); }// 繼續(xù)按自定義隊(duì)列的步驟走 void _dispatch_async_redirect_invoke(dispatch_continuation_t dc,dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags) {dispatch_thread_frame_s dtf;struct dispatch_continuation_s *other_dc = dc->dc_other;dispatch_invoke_flags_t ctxt_flags = (dispatch_invoke_flags_t)dc->dc_ctxt;dispatch_queue_t assumed_rq = (dispatch_queue_t)dc->dc_func;dispatch_queue_t dq = dc->dc_data, rq, old_dq;dispatch_priority_t old_dbp;if (ctxt_flags) {flags &= ~_DISPATCH_INVOKE_AUTORELEASE_MASK;flags |= ctxt_flags;}old_dq = _dispatch_get_current_queue();if (assumed_rq) {old_dbp = _dispatch_root_queue_identity_assume(assumed_rq);_dispatch_set_basepri(dq->dq_priority);} else {old_dbp = _dispatch_set_basepri(dq->dq_priority);}_dispatch_thread_frame_push(&dtf, dq);// _dispatch_continuation_pop_forwarded里面就是執(zhí)行_dispatch_continuation_pop函數(shù)_dispatch_continuation_pop_forwarded(dc, DISPATCH_NO_VOUCHER,DISPATCH_OBJ_CONSUME_BIT, {_dispatch_continuation_pop(other_dc, dic, flags, dq);});_dispatch_thread_frame_pop(&dtf);if (assumed_rq) _dispatch_queue_set_current(old_dq);_dispatch_reset_basepri(old_dbp);rq = dq->do_targetq;while (slowpath(rq->do_targetq) && rq != old_dq) {_dispatch_queue_non_barrier_complete(rq);rq = rq->do_targetq;}_dispatch_queue_non_barrier_complete(dq);_dispatch_release_tailcall(dq); }// 順便說下,如果按照的是dispatch_get_global_queue會執(zhí)行_dispatch_queue_override_invoke函數(shù) void _dispatch_queue_override_invoke(dispatch_continuation_t dc,dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags) {dispatch_queue_t old_rq = _dispatch_queue_get_current();dispatch_queue_t assumed_rq = dc->dc_other;dispatch_priority_t old_dp;voucher_t ov = DISPATCH_NO_VOUCHER;dispatch_object_t dou;dou._do = dc->dc_data;old_dp = _dispatch_root_queue_identity_assume(assumed_rq);if (dc_type(dc) == DISPATCH_CONTINUATION_TYPE(OVERRIDE_STEALING)) {flags |= DISPATCH_INVOKE_STEALING;} else {// balance the fake continuation push in// _dispatch_root_queue_push_override_dispatch_trace_continuation_pop(assumed_rq, dou._do);}// 同樣調(diào)用_dispatch_continuation_pop函數(shù)_dispatch_continuation_pop_forwarded(dc, ov, DISPATCH_OBJ_CONSUME_BIT, {if (_dispatch_object_has_vtable(dou._do)) {dx_invoke(dou._do, dic, flags);} else {_dispatch_continuation_invoke_inline(dou, ov, flags);}});_dispatch_reset_basepri(old_dp);_dispatch_queue_set_current(old_rq); }4、_dispatch_continuation_pop
無論是自定義的隊(duì)列還是獲取系統(tǒng)的,最終都會調(diào)用這個(gè)函數(shù)
void _dispatch_continuation_pop(dispatch_object_t dou, dispatch_invoke_context_t dic,dispatch_invoke_flags_t flags, dispatch_queue_t dq) {_dispatch_continuation_pop_inline(dou, dic, flags, dq); }static inline void _dispatch_continuation_pop_inline(dispatch_object_t dou,dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags,dispatch_queue_t dq) {dispatch_pthread_root_queue_observer_hooks_t observer_hooks =_dispatch_get_pthread_root_queue_observer_hooks();if (observer_hooks) observer_hooks->queue_will_execute(dq);_dispatch_trace_continuation_pop(dq, dou);flags &= _DISPATCH_INVOKE_PROPAGATE_MASK;if (_dispatch_object_has_vtable(dou)) {dx_invoke(dou._do, dic, flags);} else {_dispatch_continuation_invoke_inline(dou, DISPATCH_NO_VOUCHER, flags);}if (observer_hooks) observer_hooks->queue_did_execute(dq); }static inline void _dispatch_continuation_invoke_inline(dispatch_object_t dou, voucher_t ov,dispatch_invoke_flags_t flags) {dispatch_continuation_t dc = dou._dc, dc1;dispatch_invoke_with_autoreleasepool(flags, {uintptr_t dc_flags = dc->dc_flags;_dispatch_continuation_voucher_adopt(dc, ov, dc_flags);if (dc_flags & DISPATCH_OBJ_CONSUME_BIT) {dc1 = _dispatch_continuation_free_cacheonly(dc);} else {dc1 = NULL;}// 后面分析dispatch_group_async的時(shí)候會走if這個(gè)分支,但這次走的是else分支if (unlikely(dc_flags & DISPATCH_OBJ_GROUP_BIT)) {_dispatch_continuation_with_group_invoke(dc);} else {// 這次走這里,直接執(zhí)行block函數(shù)_dispatch_client_callout(dc->dc_ctxt, dc->dc_func);_dispatch_introspection_queue_item_complete(dou);}if (unlikely(dc1)) {_dispatch_continuation_free_to_cache_limit(dc1);}});_dispatch_perfmon_workitem_inc(); }至此,任務(wù)怎么被調(diào)度執(zhí)行的已經(jīng)看明白了。start_wqthread是匯編寫的,直接和內(nèi)核交互。雖然我們明確了使用了異步的任務(wù)被執(zhí)行的調(diào)用順序,但是想必還是有這樣的疑問_dispatch_worker_thread3是怎么跟內(nèi)核扯上關(guān)系的。為什么調(diào)用的是_dispatch_worker_thread3,而不是_dispatch_worker_thread或者_(dá)dispatch_worker_thread4呢? ??在此之前需要說的是,在GCD中一共有2個(gè)線程池管理著任務(wù),一個(gè)是主線程池,另一個(gè)就是除了主線程任務(wù)的線程池。主線程池由序號1的隊(duì)列管理,其他有序號2的隊(duì)列進(jìn)行管理。加上runloop運(yùn)行的runloop隊(duì)列,一共就有16個(gè)隊(duì)列。
序號 標(biāo)簽 1 com.apple.main-thread 2 com.apple.libdispatch-manager 3 com.apple.root.libdispatch-manager 4 com.apple.root.maintenance-qos 5 com.apple.root.maintenance-qos.overcommit 6 com.apple.root.background-qos 7 com.apple.root.background-qos.overcommit 8 com.apple.root.utility-qos 9 com.apple.root.utility-qos.overcommit 10 com.apple.root.default-qos 11 com.apple.root.default-qos.overcommit 12 com.apple.root.user-initiated-qos 13 com.apple.root.user-initiated-qos.overcommit 14 com.apple.root.user-interactive-qos 15 com.apple.root.user-interactive-qos.overcommit有那么多root隊(duì)列,所以application啟動的時(shí)候就會初始化這些root隊(duì)列的_dispatch_root_queues_init函數(shù)。
5、_dispatch_root_queues_init函數(shù)
void _dispatch_root_queues_init(void) {static dispatch_once_t _dispatch_root_queues_pred;dispatch_once_f(&_dispatch_root_queues_pred, NULL,_dispatch_root_queues_init_once); }static void _dispatch_root_queues_init_once(void *context DISPATCH_UNUSED) {int wq_supported;_dispatch_fork_becomes_unsafe();if (!_dispatch_root_queues_init_workq(&wq_supported)) {size_t i;for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {bool overcommit = true;_dispatch_root_queue_init_pthread_pool(&_dispatch_root_queue_contexts[i], 0, overcommit);}DISPATCH_INTERNAL_CRASH((errno << 16) | wq_supported,"Root queue initialization failed");} }static inline bool _dispatch_root_queues_init_workq(int *wq_supported) {int r; (void)r;bool result = false;*wq_supported = 0;bool disable_wq = false; (void)disable_wq;bool disable_qos = false;bool disable_kevent_wq = false;if (!disable_wq && !disable_qos) {*wq_supported = _pthread_workqueue_supported();if (!disable_kevent_wq && (*wq_supported & WORKQ_FEATURE_KEVENT)) {r = _pthread_workqueue_init_with_kevent(_dispatch_worker_thread3,(pthread_workqueue_function_kevent_t)_dispatch_kevent_worker_thread,offsetof(struct dispatch_queue_s, dq_serialnum), 0);result = !r;} }return result; }這里,已經(jīng)看到_pthread_workqueue_init_with_kevent函數(shù)就是綁定了_dispatch_worker_thread3函數(shù)去做一些GCD的線程任務(wù),看到源代碼_pthread_workqueue_init_with_kevent做了些什么。
6、_pthread_workqueue_init_with_kevent
int _pthread_workqueue_init_with_kevent(pthread_workqueue_function2_t queue_func,pthread_workqueue_function_kevent_t kevent_func,int offset, int flags) {return _pthread_workqueue_init_with_workloop(queue_func, kevent_func, NULL, offset, flags); }int _pthread_workqueue_init_with_workloop(pthread_workqueue_function2_t queue_func,pthread_workqueue_function_kevent_t kevent_func,pthread_workqueue_function_workloop_t workloop_func,int offset, int flags) {if (flags != 0) {return ENOTSUP;}__workq_newapi = true;__libdispatch_offset = offset;int rv = pthread_workqueue_setdispatch_with_workloop_np(queue_func, kevent_func, workloop_func);return rv; }static int pthread_workqueue_setdispatch_with_workloop_np(pthread_workqueue_function2_t queue_func,pthread_workqueue_function_kevent_t kevent_func,pthread_workqueue_function_workloop_t workloop_func) {int res = EBUSY;if (__libdispatch_workerfunction == NULL) {// Check whether the kernel supports new SPIsres = __workq_kernreturn(WQOPS_QUEUE_NEWSPISUPP, NULL, __libdispatch_offset, kevent_func != NULL ? 0x01 : 0x00);if (res == -1){res = ENOTSUP;} else {__libdispatch_workerfunction = queue_func;__libdispatch_keventfunction = kevent_func;__libdispatch_workloopfunction = workloop_func;// Prepare the kernel for workq action(void)__workq_open();if (__is_threaded == 0) {__is_threaded = 1;}}}return res; }我們看到了__libdispatch_workerfunction = queue_func;指定了隊(duì)列工作函數(shù)。然后我們往回看之前說的我們制造了一個(gè)人為crash,追溯棧里看到_pthread_wqthread這個(gè)函數(shù)。看下這個(gè)函數(shù)怎么啟用_dispatch_worker_thread3的
7、_dispatch_worker_thread3
實(shí)際代碼很多,這里我精簡了下,拿到了__libdispatch_workerfunction對應(yīng)的_dispatch_worker_thread3,然后直接執(zhí)行。
void _pthread_wqthread(pthread_t self, mach_port_t kport, void *stacklowaddr, void *keventlist, int flags, int nkevents) {pthread_workqueue_function_t func = (pthread_workqueue_function_t)__libdispatch_workerfunction;int options = overcommit ? WORKQ_ADDTHREADS_OPTION_OVERCOMMIT : 0;// 執(zhí)行函數(shù)(*func)(thread_class, options, NULL);__workq_kernreturn(WQOPS_THREAD_RETURN, NULL, 0, 0);_pthread_exit(self, NULL); }3、dispatch_group_async
?
?
?
?
?
?
?
?
總結(jié)
- 上一篇: Office2010打开多个excel文
- 下一篇: 【c语言】数列求和