iOS底層探索之多線程(五)—GCD不同隊(duì)列源碼分析

回顧

在上篇博客已經(jīng)介紹了各種隊(duì)列和異步、同步函數(shù)的組合,GCD的隊(duì)列和函數(shù),對(duì)隊(duì)列和任務(wù)的執(zhí)行有了清晰的認(rèn)識(shí), 那么本篇博客將繼續(xù)介紹GCD的隊(duì)列和源碼分析。

iOS底層探索之多線程(一)—進(jìn)程和線程

iOS底層探索之多線程(二)—線程和鎖

iOS底層探索之多線程(三)—初識(shí)GCD

iOS底層探索之多線程(四)—GCD的隊(duì)列

在這里插入圖片描述

1. 主隊(duì)列分析

查看主隊(duì)列的api如下圖:

dispatch_get_main_queue

  • 主隊(duì)列是一個(gè)特殊的串行隊(duì)列
  • 主隊(duì)列在調(diào)用main()函數(shù)之前自動(dòng)創(chuàng)建的。
  • 主隊(duì)列在應(yīng)用程序上下文中用于與主線程和main runloop 交互。

那么斷點(diǎn)在main函數(shù)處去驗(yàn)證一下

驗(yàn)證main_queue

通過(guò)斷點(diǎn),確實(shí)驗(yàn)證了主隊(duì)列是在調(diào)用main()函數(shù)之前自動(dòng)創(chuàng)建的。

那么我們要看底層源碼,該怎么看啊,首先我們得知道 GCD是屬于哪個(gè)源碼的,才能進(jìn)一步去探索分析。

  • 再次通過(guò)斷點(diǎn)尋找,如下圖

    尋找 GCD源碼出處

    通過(guò) bt打印堆棧信息,可以定位到libdispatch.dylib動(dòng)態(tài)庫(kù),那么就去蘋(píng)果開(kāi)源網(wǎng)站去下載源碼試試。

  • 下載libdispatch.dylib源碼,探索GCD

    libdispatch

  • libdispatch 源碼

    GCD源碼libdispatch

    libdispatch源碼比較難受

  • 注釋非常的少

  • 宏定義非常的多

  • 函數(shù)名非常的長(zhǎng)

這是一個(gè)硬骨頭,非常的不好啃,只能硬著頭皮慢慢啃了,難受?。?/p>

  • 源碼搜索dispatch_get_main_queue
    dispatch_get_main_queue

dispatch_get_main_queue是通過(guò)DISPATCH_GLOBAL_OBJECT返回的,是一個(gè)宏定義

  • DISPATCH_GLOBAL_OBJECT
    DISPATCH_GLOBAL_OBJECT宏定義
  • 通過(guò)_dispatch_main_q參數(shù)搜索
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
_dispatch_main_q
  • 還可以通過(guò)主隊(duì)列的dq_label搜索如下:

在這里插入圖片描述

在源碼中有dq_serialnum = 1,這是不是意味著可以作為主隊(duì)列就是 串行隊(duì)列的依據(jù)呢? 現(xiàn)在還不得而知,那么去看看串行隊(duì)列底層是怎么實(shí)現(xiàn)的,或許可以找到答案!

  • 主隊(duì)列的初始化是在dispatch_init()方法中?
    在這里插入圖片描述
  • dispatch_init()中成功找到了主隊(duì)列初始化的地方,
  • 獲取默認(rèn)隊(duì)列,
  • 并將主隊(duì)列地址綁定到當(dāng)前隊(duì)列和主線程中

2. 串行、并發(fā)隊(duì)列分析

串行隊(duì)列并發(fā)隊(duì)列都通過(guò)dispatch_queue_create創(chuàng)建的,那么去搜索一下

dispatch_queue_create

通過(guò)搜索定位到dispatch_queue_create,在通過(guò)返回的是_dispatch_lane_create_with_target,再繼續(xù)搜索
_dispatch_lane_create_with_target

  • 代碼比較長(zhǎng),從返回值看,再推導(dǎo)


    從返回值推導(dǎo)
  • _dispatch_object_alloc申請(qǐng)內(nèi)存空間
  • _dispatch_queue_init構(gòu)造函數(shù)初始化
  • 判斷是否為并發(fā)隊(duì)列,如果是,傳入DISPATCH_QUEUE_WIDTH_MAX,否則傳入1。也就是說(shuō),串行隊(duì)列這里傳入1,如果是并發(fā)隊(duì)列,則傳入DISPATCH_QUEUE_WIDTH_MAX
    DISPATCH_QUEUE_WIDTH_MAX
  • 對(duì)dq進(jìn)行設(shè)置,如dq_label、dq_priority

_dispatch_queue_init

在這里插入圖片描述

  • 把前面的 width 傳進(jìn)來(lái),賦值dqf |= DQF_WIDTH(width)
  • DQF_WIDTH(width),也就是用來(lái)確定隊(duì)列的類型,以此來(lái)區(qū)分串行隊(duì)列并發(fā)隊(duì)列
    DISPATCH_QUEUE_SERIAL_NUMBER_INIT

    其他參數(shù)vtabledqai,分別是什么呢?繼續(xù)探索
    在這里插入圖片描述
  • dqai初始化

在開(kāi)頭有這么一句代碼

// dqai 創(chuàng)建 - dqa傳入的屬性串行還是并行
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

  • _dispatch_queue_attr_to_info

    _dispatch_queue_attr_to_info

    在這里進(jìn)行初始化了dqai,并判斷dqa的類型,如果是并發(fā)隊(duì)列,則設(shè)置并發(fā)隊(duì)列true,否則默認(rèn)為串行隊(duì)列。在調(diào)用_dispatch_queue_init對(duì)dq進(jìn)行構(gòu)造時(shí),對(duì)隊(duì)列類型進(jìn)行了區(qū)分,也就是DQF_WIDTH(width)的傳參,串行隊(duì)列width=1,否則為并發(fā)隊(duì)列。

  • vtable

        const void *vtable; // - 設(shè)置類 類是通過(guò)宏定義拼接而成
        if (dqai.dqai_concurrent) {
                // OS_dispatch_##name##_class
                // OS_dispatch_queue_concurrent - 宏定義拼接類類型
                vtable = DISPATCH_VTABLE(queue_concurrent);
        } else {
                vtable = DISPATCH_VTABLE(queue_serial);
        }

vtable可以理解為是一個(gè)類,或者說(shuō)構(gòu)造隊(duì)列的模板類,qai來(lái)區(qū)分隊(duì)列的類型,根據(jù)隊(duì)列的類型來(lái)初始化不同的vtable。DISPATCH_VTABLE是一個(gè)宏定義的方法,全局搜索DISPATCH_VTABLE的定義

// DISPATCH_VTABLE定義
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)

// vtable symbols - 模板
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))

// 拼接形成類
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class

DISPATCH_VTABLE函數(shù)的傳參根據(jù)不同的隊(duì)列類型傳參不一致。

并發(fā)隊(duì)列
queue_concurrent參數(shù),最終拼接后,隊(duì)列類型對(duì)應(yīng)的類為:OS_dispatch_queue_concurrent

串行隊(duì)列
queue_serial參數(shù),最終拼接后,隊(duì)列類型對(duì)應(yīng)的類為:OS_dispatch_queue_serial

所以vtable對(duì)應(yīng)的就是隊(duì)列的類型。通過(guò)拼接完成類的定義,這和我們?cè)趹?yīng)用層使用的隊(duì)列類型是一致的。

測(cè)試代碼

3. 全局隊(duì)列分析

進(jìn)入dispatch_get_global_queueapi

dispatch_get_global_queue
  • 創(chuàng)建全局并發(fā)隊(duì)列時(shí)可以傳參數(shù)
  • 根據(jù)不同服務(wù)質(zhì)量或者優(yōu)先等級(jí)提供不同的并發(fā)隊(duì)列。

通過(guò)全局隊(duì)列的標(biāo)識(shí) 在源碼里面搜索??

全局隊(duì)列集合
  • 系統(tǒng)會(huì)維護(hù)一個(gè)全局隊(duì)列集合,
  • 根據(jù)不同的服務(wù)質(zhì)量或者優(yōu)先等級(jí)提供不同的全局隊(duì)列。
  • 我們?cè)陂_(kāi)發(fā)工作中默認(rèn)使用:dispatch_get_global_queue(0, 0)。

更多內(nèi)容持續(xù)更新

?? 喜歡就點(diǎn)個(gè)贊吧????

?? 覺(jué)得有收獲的,可以來(lái)一波,收藏+關(guān)注,評(píng)論 + 轉(zhuǎn)發(fā),以免你下次找不到我????

??歡迎大家留言交流,批評(píng)指正,互相學(xué)習(xí)??,提升自我??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容