iOS 底層原理 - GCD

GCD簡介

什么是GCD?
全稱是 Grand Central Dispatch
純 C 語言绞绒,提供了非常多強(qiáng)大的函數(shù) GCD的優(yōu)勢
GCD 是蘋果公司為多核的并行運(yùn)算提出的解決方案
GCD 會自動(dòng)利用更多的CPU內(nèi)核(比如雙核度硝、四核)
GCD 會自動(dòng)管理線程的生命周期(創(chuàng)建線程依沮、調(diào)度任務(wù)盗舰、銷毀線程) 程序員只需要告訴 GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼

函數(shù)

將任務(wù)添加到隊(duì)列扎谎,并且指定執(zhí)行任務(wù)的函數(shù)
任務(wù)使用 block 封裝

  • 任務(wù)的 block 沒有參數(shù)也沒有返回值
  • 執(zhí)行任務(wù)的函數(shù)
  • 異步 dispatch_async
  • 不用等待當(dāng)前語句執(zhí)行完畢碳想,就可以執(zhí)行下一條語句 * 會開啟線程執(zhí)行 block 的任務(wù)
  • 異步是多線程的代名詞
  • 同步 dispatch_sync
  • 必須等待當(dāng)前語句執(zhí)行完畢,才會執(zhí)行下一條語
  • 不會開啟線程
  • 在當(dāng)前執(zhí)行 block 的任務(wù)

隊(duì)列

屏幕快照 2020-08-19 下午11.11.05.png

隊(duì)列的創(chuàng)建

#define DISPATCH_TARGET_QUEUE_DEFAULT NULL

dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
    return _dispatch_lane_create_with_target(label, attr,
            DISPATCH_TARGET_QUEUE_DEFAULT, true);
}

其參數(shù)如下:

const char *label: 隊(duì)列的唯一標(biāo)識符毁靶,可以傳空值胧奔。
dispatch_queue_attr_t attr: 標(biāo)識隊(duì)列的類型,區(qū)分是串行隊(duì)列還是并發(fā)隊(duì)列预吆。

DISPATCH_QUEUE_SERIAL: 串行隊(duì)列
DISPATCH_QUEUE_CONCURRENT: 并發(fā)隊(duì)列

串行隊(duì)列的創(chuàng)建方法

dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);

常用的主隊(duì)列就是串行隊(duì)列龙填,dispatch_get_main_queue()

專門用在主線程調(diào)度任務(wù)的隊(duì)列,也稱UI隊(duì)列
不會再開啟線程
如果有任務(wù)執(zhí)行啡浊,再添加其他任務(wù)觅够,會被堵塞

并發(fā)隊(duì)列的創(chuàng)建方法

dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);

常用的全局隊(duì)列就是并發(fā)隊(duì)列dispatch_get_global_queue(long identifier, unsigned long flags),它可以直接執(zhí)行異步任務(wù)巷嚣。該方法第一個(gè)參數(shù)是優(yōu)先級喘先,全局隊(duì)列的優(yōu)先級為DISPATCH_QUEUE_PRIORITY_DEFAULT,這個(gè)值是一個(gè)為0的宏廷粒,所以也可以傳0窘拯。unsigned long flags: 蘋果官方文檔的解釋是Flags that are reserved for future use。標(biāo)記這個(gè)參數(shù)是為了未來使用保留的坝茎,現(xiàn)在傳0即可涤姊。
此處引入線程的優(yōu)先級概念,優(yōu)先級越高越先執(zhí)行嗤放。

DISPATCH_QUEUE_PRIORITY_HIGH: 2
DISPATCH_QUEUE_PRIORITY_DEFAULT: 0
DISPATCH_QUEUE_PRIORITY_LOW: (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND: INT16_MIN

隊(duì)列和函數(shù)組合

屏幕快照 2020-08-19 下午11.15.37.png

看下例子一

dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"4");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
    });
    NSLog(@"5");

打印結(jié)果為1思喊,5,2次酌,4恨课,3,先是順序執(zhí)行岳服,異步隊(duì)列不會阻塞剂公,但是會耗時(shí),所以會慢些

例子二吊宋,選擇題

dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);

    /***
     1 2 3
     0
     7 8 9
     */
    
    dispatch_async(queue, ^{
        // sleep(2);
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    // 堵塞 - 護(hù)犢子
    dispatch_sync(queue, ^{
        NSLog(@"3");
    });
    // **********************
    NSLog(@"0");

    dispatch_async(queue, ^{
        NSLog(@"7");
    });
    dispatch_async(queue, ^{
        NSLog(@"8");
    });
    dispatch_async(queue, ^{
        NSLog(@"9");
    });

    // A: 1230789
    // B: 1237890
    // C: 3120798
    // D: 2137890

答案AC纲辽,1,2開啟異步線程,代碼在137行堵塞拖吼,阻塞耗時(shí)比異步隊(duì)列耗時(shí)久鳞上,所以0在1,2绿贞,3之后運(yùn)行因块,0在主線程,7籍铁,8涡上,9在它之后開啟異步線程,耗時(shí)拒名,所以0在7吩愧,8,9之前

例子三

 // 同步隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");

打印結(jié)果增显,1雁佳,5,2同云,產(chǎn)生死鎖崩潰糖权,首先是一個(gè)串行隊(duì)列,1炸站,5線執(zhí)行星澳,然后執(zhí)行異步函數(shù)里的任務(wù),2旱易,同步代碼塊禁偎,4,在4這里產(chǎn)生阻塞阀坏,需要等同步代碼塊執(zhí)行完如暖,同步代碼塊里有任務(wù)3,但是串行隊(duì)列的原則是先進(jìn)先出忌堂,所以3需要等4任務(wù)執(zhí)行完后才能執(zhí)行盒至,產(chǎn)生死鎖。


屏幕快照 2020-08-20 上午12.10.28.png

如果把4任務(wù)提前會死鎖嗎

// 同步隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"4");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        
    });
    NSLog(@"5");

打印結(jié)果 1士修,5妄迁,2,4李命,崩潰,因?yàn)槿蝿?wù)3和異步函數(shù)形成相互等待箫老,使用方面要特別注意串行隊(duì)列和同步函數(shù)封字。判斷是否發(fā)生死鎖的最好方法就是看有沒有在串行隊(duì)列(當(dāng)然也包括主隊(duì)列)中向這個(gè)隊(duì)列添加任務(wù)。

死鎖

? 主線程因?yàn)槟阃胶瘮?shù)的原因等著先執(zhí)行任務(wù)
? 主隊(duì)列等著主線程的任務(wù)執(zhí)行完畢再執(zhí)行自己的任務(wù)
? 主隊(duì)列和主線程相互等待會造成死鎖

隊(duì)列是如何創(chuàng)建的

我們通過dispatch_queue_create創(chuàng)建隊(duì)列,我們點(diǎn)進(jìn)去發(fā)現(xiàn)看不到其他的阔籽,需要配合源碼分析流妻,首先我們在dispatch_queue_create處下一個(gè)dispatch_queue_create的符號斷點(diǎn),可以看到它在libdispatch動(dòng)態(tài)庫里面笆制,在Apple open source里下載對應(yīng)的源碼绅这。

屏幕快照 2020-08-21 上午7.56.43.png

打開源碼,搜索dispatch_queue_create( 發(fā)現(xiàn)搜出來很多在辆,因?yàn)槲覀兇┑牡谝粋€(gè)參數(shù)是字符串证薇,搜索dispatch_queue_create(con,結(jié)果如下


屏幕快照 2020-08-21 上午8.03.09.png

來到這里

dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
    return _dispatch_lane_create_with_target(label, attr,
            DISPATCH_TARGET_QUEUE_DEFAULT, true);
}

再往下進(jìn)入方法_dispatch_lane_create_with_target

屏幕快照 2020-08-23 下午10.04.11.png

QOS:類似于優(yōu)先級匆篓,在創(chuàng)建global queue的時(shí)候的第一個(gè)參數(shù)就是priority或者QOS浑度。

over commit:如果當(dāng)前隊(duì)列沒有可以分配的空余線程,就開啟一個(gè)新線程來做任務(wù)

我們創(chuàng)建隊(duì)列主要是通過第二個(gè)參數(shù)區(qū)分是串行還是并行鸦概,也就是重點(diǎn)在于dispatch_queue_attr_t dqa這里箩张。
搜索_dispatch_queue_attr_to_info方法,來到這里

ispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
    dispatch_queue_attr_info_t dqai = { };
        //默認(rèn)是串行隊(duì)列窗市,如果傳NULL返回
    if (!dqa) return dqai;

#if DISPATCH_VARIANT_STATIC
    if (dqa == &_dispatch_queue_attr_concurrent) {
        dqai.dqai_concurrent = true;
        return dqai;
    }
#endif

    if (dqa < _dispatch_queue_attrs ||
            dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
        DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
    }

    // 蘋果的算法
    size_t idx = (size_t)(dqa - _dispatch_queue_attrs);

    // 位域
    // 0000 000000000 00000000000 0000 000  1
    
    dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;

    dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;

    dqai.dqai_relpri = -(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;

    dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;

    dqai.dqai_autorelease_frequency =
            idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;

    dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;

    return dqai;
}

第一句代碼就是將我們的外界傳進(jìn)來區(qū)分串行還是并發(fā)的參數(shù)傳進(jìn)去創(chuàng)建了一個(gè) dispatch_queue_attr_info_t 類型的結(jié)構(gòu)體先慷。
一查 dispatch_queue_attr_info_t 是一個(gè)結(jié)構(gòu)體位域,結(jié)構(gòu)體位域可以通過一些位運(yùn)算取出我們想要的內(nèi)容咨察,過濾掉我們不想要的數(shù)據(jù)论熙。

1.串行隊(duì)列傳的是NULL ,所以直接返回.
2.并行隊(duì)列,通過按位取余設(shè)置下面的各個(gè)參數(shù)

typedef struct dispatch_queue_attr_info_s {
    dispatch_qos_t dqai_qos : 8;
    int      dqai_relpri : 8;
    uint16_t dqai_overcommit:2;
    uint16_t dqai_autorelease_frequency:2;
    uint16_t dqai_concurrent:1;
    uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;

回到原來的方法_dispatch_lane_create_with_target繼續(xù)分析

static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
    ////其實(shí)就是把a(bǔ)ttr轉(zhuǎn)化為{}字典形式的attr集合,dqa里面會有qos,overcommit,inactive,concurrent之類的key和value
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

    //
    // Step 1: Normalize arguments (qos, overcommit, tq)
    //

    dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
    }
    if (qos == DISPATCH_QOS_MAINTENANCE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
    }
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS

    _dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_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");
        }
    }

    if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
        // Handle discrepancies between attr and target queue, attributes win
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
                overcommit = _dispatch_queue_attr_overcommit_enabled;
            } else {
                overcommit = _dispatch_queue_attr_overcommit_disabled;
            }
        }
        if (qos == DISPATCH_QOS_UNSPECIFIED) {
            qos = _dispatch_priority_qos(tq->dq_priority);
        }
        tq = NULL;
    } else if (tq && !tq->do_targetq) {
        // target is a pthread or runloop root queue, setting QoS or overcommit
        // is disallowed
        if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
            DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
                    "and use this kind of target queue");
        }
    } else {
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            // Serial queues default to overcommit!
            overcommit = dqai.dqai_concurrent ?
                    _dispatch_queue_attr_overcommit_disabled :
                    _dispatch_queue_attr_overcommit_enabled;
        }
    }
    if (!tq) {
        tq = _dispatch_get_root_queue(
                qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

    //
    // Step 2: Initialize the queue
    //

    if (legacy) {
        // if any of these attributes is specified, use non legacy classes
        if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
            legacy = false;
        }
    }

    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
    if (dqai.dqai_concurrent) {
        // 通過dqai.dqai_concurrent 來區(qū)分并發(fā)和串行
        // OS_dispatch_queue_concurrent_class
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    switch (dqai.dqai_autorelease_frequency) {
    case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
        dqf |= DQF_AUTORELEASE_NEVER;
        break;
    case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
        dqf |= DQF_AUTORELEASE_ALWAYS;
        break;
    }
    if (label) {
        const char *tmp = _dispatch_strdup_if_mutable(label);
        if (tmp != label) {
            dqf |= DQF_LABEL_NEEDS_FREE;
            label = tmp;
        }
    }
    
    // 開辟內(nèi)存 - 生成響應(yīng)的對象 queue
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    
    // 構(gòu)造方法
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

    // 標(biāo)簽
    dq->dq_label = label;
    // 優(yōu)先級
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    if (!dqai.dqai_inactive) {
        _dispatch_queue_priority_inherit_from_target(dq, tq);
        _dispatch_lane_inherit_wlh_from_target(dq, tq);
    }
    _dispatch_retain(tq);
    dq->do_targetq = tq;
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq;
}

通過dqai.dqai_concurrent 來區(qū)分并發(fā)和串行扎拣。_dispatch_queue_attr_overcommit_enabled串行赴肚,_dispatch_queue_attr_overcommit_disabled并發(fā)

overcommit = dqai.dqai_concurrent ?
                    _dispatch_queue_attr_overcommit_disabled :
                    _dispatch_queue_attr_overcommit_enabled;

dispatch_object_t

這里通過_dispatch_object_alloc開辟內(nèi)存,那我們看一下dispatch_object_t的內(nèi)容二蓝。
一般通過繼承實(shí)現(xiàn)多態(tài)誉券,這里通過聯(lián)合體的方式,包含了很多類型刊愚,避免開辟太多空間踊跟,是互斥的,同一時(shí)間只能有一個(gè)有效

typedef struct dispatch_object_s {
private:
    dispatch_object_s();
    ~dispatch_object_s();
    dispatch_object_s(const dispatch_object_s &);
    void operator=(const dispatch_object_s &);
} *dispatch_object_t;
#define DISPATCH_DECL(name) \
        typedef struct name##_s : public dispatch_object_s {} *name##_t
#define DISPATCH_DECL_SUBCLASS(name, base) \
        typedef struct name##_s : public base##_s {} *name##_t
#define DISPATCH_GLOBAL_OBJECT(type, object) (static_cast<type>(&(object)))
#define DISPATCH_RETURNS_RETAINED
#else /* Plain C */
#ifndef __DISPATCH_BUILDING_DISPATCH__
typedef union {
    struct _os_object_s *_os_obj;
    struct dispatch_object_s *_do;
    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_semaphore_s *_dsema;
    struct dispatch_data_s *_ddata;
    struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;

這里我們看一個(gè)例子鸥诽。

// com.apple.root.default-qos width = 0xffe
    // com.apple.root.default-qos.overcommit   width = 0x1 = 1
    // 隊(duì)列 - 模板 _dispatch_root_queues里面取值
//    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
//        .dq_label = "com.apple.root.default-qos",
//        .dq_serialnum = 10,
//    ),
//    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
//            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
//        .dq_label = "com.apple.root.default-qos.overcommit",
//        .dq_serialnum = 11,
//    ),

    // dispatch_init
    // _dispatch_root_queues init
    // width = 0xfff
    // 自定義: 0xffe
dispatch_queue_t queue1 = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_queue_t queue2 = dispatch_queue_create("KC", NULL);
    
    NSLog(@"%@",dispatch_get_main_queue());
    
    NSLog(@"%@",dispatch_get_global_queue(0, 0));

    NSLog(@"%@-%@",queue1,queue2);

打印結(jié)果如下

2020-08-23 22:47:45.560834+0800 001---函數(shù)與隊(duì)列[95788:1954758] 來了 master
2020-08-23 22:47:45.561110+0800 001---函數(shù)與隊(duì)列[95788:1954758] <OS_dispatch_queue_main: com.apple.main-thread>
2020-08-23 22:47:45.561291+0800 001---函數(shù)與隊(duì)列[95788:1954758] <OS_dispatch_queue_global: com.apple.root.default-qos>
2020-08-23 22:47:45.561472+0800 001---函數(shù)與隊(duì)列[95788:1954758] <OS_dispatch_queue_concurrent: cooci>-<OS_dispatch_queue_serial: KC>
(lldb) po queue1
<OS_dispatch_queue_concurrent: cooci[0x600003686600] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos[0x105530f00], width = 0xffe, state = 0x0000041000000000, in-flight = 0}>

(lldb) po quere2
error: use of undeclared identifier 'quere2'
(lldb) po queue2
<OS_dispatch_queue_serial: KC[0x600003686700] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos.overcommit[0x105530f80], width = 0x1, state = 0x001ffe2000000000, in-flight = 0}>

通過上面分析我們可以看到一個(gè)com.apple.root.default-qos商玫,width = 0xffe,為并發(fā)隊(duì)列牡借,一個(gè)com.apple.root.default-qos.overcommit拳昌,width = 0x1 = 1為串行隊(duì)列。DISPATCH_QUEUE_WIDTH_MAX是通過源碼知道是一個(gè)宏定義钠龙,等于0x1000減2炬藤,結(jié)果為0xffe御铃。中間有一個(gè)宏DISPATCH_QUEUE_WIDTH_POOL 為0x1000減一,所以這里是減2沈矿。


屏幕快照 2020-09-22 下午10.25.32.png
// 構(gòu)造方法
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

_dispatch_get_root_queue

static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
        DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
    }
    // 4-1= 3
    // 2*3+0/1 = 6/7
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

通過上面的源碼可以看到_dispatch_get_root_queue方法的傳值上真,qos為4,overcommit為0羹膳,1睡互,串行為1,并發(fā)為0陵像,默認(rèn)為1就珠。

if (!tq) {
        tq = _dispatch_get_root_queue(
                qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

這里其實(shí)是return _dispatch_root_queues[2 * (qos - 1) + overcommit]
也就是說target queue就是從_dispatch_root_queues隊(duì)列里面拿第(2 * (qos - 1) + overcommit)個(gè)queue。
我們接下來看下_dispatch_root_queues這個(gè)數(shù)組蠢壹,在源碼里查找

struct dispatch_queue_global_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_global), \
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
        .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
        .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
        .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
                _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
        __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_FALLBACK,
        .dq_label = "com.apple.root.default-qos",
        .dq_serialnum = 10,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
            DISPATCH_PRIORITY_FLAG_FALLBACK | 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,
    ),
};

這里就拿到了上面的打印結(jié)果嗓违。

打印

    NSLog(@"%@",dispatch_get_main_queue());
    
    NSLog(@"%@",dispatch_get_global_queue(0, 0));

輸出結(jié)果為

2020-08-27 20:38:34.511876+0800 001---函數(shù)與隊(duì)列[96868:2126481] <OS_dispatch_queue_main: com.apple.main-thread>
2020-08-27 20:38:34.512160+0800 001---函數(shù)與隊(duì)列[96868:2126481] <OS_dispatch_queue_global: com.apple.root.default-qos>
(lldb) po dispatch_get_global_queue(0, 0)
<OS_dispatch_queue_global: com.apple.root.default-qos[0x109113f00] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>

dispatch_get_main_queue()組隊(duì)列可以直接輸出,是個(gè)全局靜態(tài)結(jié)構(gòu)體图贸,源碼搜索com.apple.main-thread蹂季,發(fā)現(xiàn)_dispatch_main_q如下,dq_serialnum為1是串行隊(duì)列

struct dispatch_queue_static_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

這里有個(gè)宏DISPATCH_GLOBAL_OBJECT_HEADER疏日,搜下看相當(dāng)于這四句代碼

#if OS_OBJECT_HAVE_OBJC1
#define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
    .do_vtable = DISPATCH_VTABLE(name), \
    ._objc_isa = DISPATCH_OBJC_CLASS(name), \
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#else
#define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
    .do_vtable = DISPATCH_VTABLE(name), \
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#endif

繼續(xù)往下分析偿洁,打印dispatch_get_global_queue(0, 0)發(fā)現(xiàn)它的width = 0xfff,也就是0x1000減1,給了全局并發(fā)隊(duì)列
// dispatch_init
// _dispatch_root_queues init 初始化就是0xfff沟优,為了區(qū)分自定義的涕滋,自定義的是0xffe,任何隊(duì)列都是由_dispatch_root_queues進(jìn)行模板創(chuàng)建的挠阁,除去main_queue宾肺。
// width = 0xfff
// 自定義: 0xffe

總結(jié)一下各種queue的獲取方式吧:
自己create創(chuàng)建的queue是需要alloc分配內(nèi)存以后init,最后從root_queue里面的拿一個(gè)作為新queue的target queue的
main和global queue是不需要alloc init的侵俗,直接從root_queue里拿出對應(yīng)的queue即可

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锨用,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子隘谣,更是在濱河造成了極大的恐慌增拥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寻歧,死亡現(xiàn)場離奇詭異掌栅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)码泛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門猾封,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人噪珊,你說我怎么就攤上這事忘衍∮馍唬” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵枚钓,是天一觀的道長。 經(jīng)常有香客問我瑟押,道長搀捷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任多望,我火速辦了婚禮嫩舟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怀偷。我一直安慰自己家厌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布椎工。 她就那樣靜靜地躺著饭于,像睡著了一般。 火紅的嫁衣襯著肌膚如雪维蒙。 梳的紋絲不亂的頭發(fā)上掰吕,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音颅痊,去河邊找鬼殖熟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛斑响,可吹牛的內(nèi)容都是我干的菱属。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼舰罚,長吁一口氣:“原來是場噩夢啊……” “哼纽门!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沸停,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤膜毁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后愤钾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘟滨,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年能颁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杂瘸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伙菊,死狀恐怖败玉,靈堂內(nèi)的尸體忽然破棺而出敌土,到底是詐尸還是另有隱情,我是刑警寧澤运翼,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布返干,位于F島的核電站,受9級特大地震影響血淌,放射性物質(zhì)發(fā)生泄漏矩欠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一悠夯、第九天 我趴在偏房一處隱蔽的房頂上張望癌淮。 院中可真熱鬧,春花似錦沦补、人聲如沸乳蓄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虚倒。三九已至,卻和暖如春店诗,著一層夾襖步出監(jiān)牢的瞬間裹刮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工庞瘸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捧弃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓擦囊,卻偏偏與公主長得像违霞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子瞬场,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容