最近看了蘋果 libdispatch 的源碼,也就是 GCD 的源碼,對(duì)于 GCD 的運(yùn)作方式有一定了解和自己的見解。我看的 libdispatch 并不是蘋果最新版本的源碼枣耀,但按道理來說 GCD 的運(yùn)作邏輯應(yīng)該不會(huì)很大的改變拄轻,細(xì)節(jié)方面是有所改變的筏养,但并不影響我們對(duì) GCD 的運(yùn)作機(jī)制的了解。這里是 GCD 的源碼:libdispatch源碼拖陆。
首先來說一下 GCD 中用到的數(shù)據(jù)類型,在 base.h 文件中有所提到:
上圖展示了 GCD 的所需數(shù)據(jù)類型的結(jié)構(gòu)依啰,我講述一下這些數(shù)據(jù)類型干什么用的:
_do: 這個(gè)是 dispatch_object_s * 結(jié)構(gòu)體丐黄,這個(gè)是 GCD 的基類,GCD 數(shù)據(jù)結(jié)構(gòu)都是由這個(gè)結(jié)構(gòu)體搭建起來的孔飒。
_dc: 這個(gè)是任務(wù)類型,通常 dispatch_async 內(nèi)的 block 最終都會(huì)封裝成這個(gè)數(shù)據(jù)類型艰争。
_dq: 這個(gè)是任務(wù)隊(duì)列坏瞄,我們創(chuàng)建的對(duì)列都是這個(gè)類型的,不管是串行隊(duì)列還是并發(fā)隊(duì)列甩卓。
_dqa: 這個(gè)是任務(wù)隊(duì)列的屬性鸠匀,任務(wù)隊(duì)列的屬性里面包含了任務(wù)隊(duì)列里面的一些操作函數(shù),可以表明這個(gè)任務(wù)隊(duì)列是串行還是并發(fā)隊(duì)列逾柿。
_ds: 這個(gè)是 GCD 的 sourece 缀棍,可以監(jiān)測(cè)內(nèi)核事件,文件讀寫事件和 socket 通信事件等机错。
_dsa: sourece 的屬性爬范。
_dsema: 信號(hào)量,如果了解過 pthread 都知道弱匪,信號(hào)量可以用來調(diào)度線程青瀑。
dispatch_object_t 是一個(gè)聯(lián)合體,所以當(dāng)用 dispatch_object_t 可以代表這個(gè)結(jié)合體內(nèi)的所有數(shù)據(jù)類型。
接下來詳細(xì)說明一下 dispatch_object_s 內(nèi)的結(jié)構(gòu)是怎么樣的(大家也可以閱讀這篇文章:變態(tài)的libDispatch結(jié)構(gòu)分析-object結(jié)構(gòu) - 愛悠閑):
從上兩圖可以清晰的了解到 dispatch_object_s 的結(jié)構(gòu)斥难,這里講解一下枝嘶,這個(gè)結(jié)構(gòu)體內(nèi)的一些數(shù)據(jù)的作用:
dispatch_object_s 最前面有一個(gè) vtable 的結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體內(nèi)包含了這個(gè) dispatch_object_s 的操作函數(shù)哑诊,vtable內(nèi)的數(shù)據(jù)結(jié)構(gòu)的解析:
do_type:這個(gè) dispatch_object_s 的類型群扶。
do_kined:說明這個(gè) dispatch_object_s 。
do_debug:debug 方法镀裤。
do_invoke: 喚醒隊(duì)列的方法竞阐。
do_dispose:銷毀隊(duì)列的方法,通常內(nèi)部會(huì)調(diào)用 這個(gè)對(duì)象的 finalizer 函數(shù)淹禾。
do_probe:這個(gè)方法很重要馁菜,用戶創(chuàng)建的隊(duì)列這個(gè)方法是空的,但 rootqueue 內(nèi)的這個(gè)有一個(gè) _dispatch_queue_wakeup_global 函數(shù)铃岔,這是個(gè)很重要的函數(shù)汪疮。
上面大體是 vtable 內(nèi)的數(shù)據(jù),其余的是:
do_next:鏈表的 next 毁习。
do_ref_cnt:引用計(jì)數(shù)智嚷。
do_ref_xcnt:外部引用計(jì)數(shù)。(這兩個(gè)是用來內(nèi)存管理的纺且,這里探究 GCD 的機(jī)制盏道,我也就沒多搞清楚這兩個(gè)是怎么用的)
do_suspend_cnt:suspend計(jì)數(shù),用作暫停標(biāo)志载碌,比如延時(shí)處理的任務(wù)猜嘱,設(shè)置該引用計(jì)數(shù)之后;在任務(wù)到時(shí)后嫁艇,計(jì)時(shí)器處理將會(huì)將該標(biāo)志位修改朗伶,然后喚醒隊(duì)列調(diào)度。
dispatch_queue_s:目標(biāo)隊(duì)列步咪,就是當(dāng)前這個(gè)struct x在哪個(gè)隊(duì)列運(yùn)行论皆。
do_ctext:上下文,我們要傳遞的參數(shù)猾漫。
下面說一下宾娜,了解 GCD 所需要的知識(shí):
在程序運(yùn)行的時(shí)候魄衅,GCD 會(huì)初始化 六個(gè) rootqueue 和 一個(gè) mainqueue俯在,這六個(gè) rootqueue 有低中高三種優(yōu)先級(jí)杆烁,這些 rootqueue 主要是用來調(diào)度任務(wù),我們自己創(chuàng)建的隊(duì)列其實(shí)并不能調(diào)配任務(wù)禽翼,因?yàn)槲覀儎?chuàng)建的隊(duì)列的 do_probe 都是空的坠陈,我自己創(chuàng)建的隊(duì)列都是鏈接在 rootqueue 下的萨惑,利用 rootqueue 來調(diào)配任務(wù),所以我也建議不要使用 GCD 創(chuàng)建隊(duì)列仇矾,這樣會(huì)使內(nèi)部處理更加復(fù)雜庸蔼,這個(gè)以后詳細(xì)說明。 mianqueue 是要綁定在 UI 線程的贮匕,用來更新界面的姐仅,mainqueue 是一種串行隊(duì)列。 mianqueue 在用戶層彰顯的就是 dispatch_get_main_queue()刻盐,而 rootqueue 彰顯的是 dispatch_get_global_queue 掏膏。下面講解一下 dispatch_queue_s 的結(jié)構(gòu)是怎么樣的:
上圖可以看出,dispatch_queue_s 和 dispatch_object_s 差別在于 dispatch_queue_s 多了DISPATCH_QUEUE_HEADER 和 dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]敦锌,我們從中也可以看出馒疹,我們隊(duì)列起名要少于64個(gè)字符,DISPATCH_QUEUE_HEADER 內(nèi)的內(nèi)容有:
dq_running: 隊(duì)列正在運(yùn)行的任務(wù)數(shù)乙墙。
dq_width: 隊(duì)列的寬度(串行隊(duì)列為1颖变,并發(fā)隊(duì)列應(yīng)該大于1)。
dq_item_tail: 指向這個(gè)隊(duì)列的尾節(jié)點(diǎn)听想。
dq_item_head: 指向這個(gè)隊(duì)列的頭節(jié)點(diǎn)腥刹。
dq_serialnum: 不知道干什么的。汉买。衔峰。。
dq_finalizer_ctxt: 結(jié)束函數(shù)的的上下文(參數(shù))蛙粘。
dq_finalizer_func: 結(jié)束函數(shù)垫卤。
可以看一下一開始初始化的6個(gè) rootqueue 和 mainqueue 的內(nèi)容:
上圖展示 6個(gè) rootqueue 和 mianqueue 的具體內(nèi)容,主要講一下里面重要的出牧,在 rootqueue 里面 重要的是 do_vtable 內(nèi)的do_provbe 的函數(shù)是什么穴肘,截圖一下:
可以看出 rootsueu 的函數(shù)是 _dispatch_queue_wakeup_global,這個(gè)很重要崔列。
在 libdispatch 里面有一些原子操作函數(shù),這些函數(shù)都是由匯編寫成的旺遮,效率很高赵讯,而且不會(huì)被其他線程干擾,這里說明一下一些原子函數(shù)的作用:
dispatch_atomic_cmpxchg(A, B, C) : 將 A 和 B對(duì)比耿眉,相等边翼,則將 C 賦值給 A,返回 YES鸣剪,否則返回 NO组底。
dispatch_atomic_xchg(A, C): 將 C 賦值給 A , 返回賦值前的 A 丈积。
dispatch_atomic_inc(A): A 自增1。
dispatch_atomic_dec(A): A 自減1债鸡。
dispatch_atomic_add(A, B): A = A + B 江滨。
Adispatch_atomic_sub(A, B): A = A - B 。
dispatch_atomic_or(A, B): A = A | B 厌均。
dispatch_atomic_and(A, B): A = A & B 唬滑。
在 GCD 內(nèi)有一宏定義: _dispatch_hardware_pause(),這個(gè)宏定義其實(shí)是 asm("pause")棺弊,在?__asm__("pause")用法_Linux_IThao123 - IT行業(yè)第一站?說明了這個(gè)用法的好處晶密,簡(jiǎn)單說一下,在 x86 架構(gòu)的 CPU 中模她,在循環(huán)用這個(gè)匯編指令稻艰,可以保證循環(huán)不會(huì)被退出且可降低功耗,蘋果手機(jī)用的是 ARM 指令侈净,用這個(gè)匯編指令應(yīng)該作用不大尊勿。
下面的文章將詳細(xì)分析 GCD 異步執(zhí)行和同步執(zhí)行等的實(shí)現(xiàn)機(jī)制。