美文网首页
[iOS] GCD是神马-源码分析

[iOS] GCD是神马-源码分析

作者: 木小易Ying | 来源:发表于2019-08-27 07:20 被阅读0次

这篇大概是最艰难的一篇了,GCD的底层用了很多宏,并且总是调来调去的,讲真非常的艰难了,所以菜鸟酱只能尽力了,可以随意吐槽一下哈~ 这篇也只解析一下最常用的create和async,因为实在是太长了QAQ

源码下载:https://libdispatch.macosforge.org or https://github.com/apple

常见内容:likely / unlikely / slowpath / fastpath

#define likely(x) __builtin_expect(!!(x), 1) //条件很可能为真
#define unlikely(x) __builtin_expect(!!(x), 0) //条件很可能为假

#define fastpath(x) ((typeof(x))__builtin_expect((long)(x), ~0l)) //条件很可能为真
#define slowpath(x) ((typeof(x))__builtin_expect((long)(x), 0l)) //条件很可能为假

好像是不同版本使用的不太一样,但是源码中总是逃不开unlikely或者slowpath这一套,这个其实就是为了对CPU指令优化,告诉编译时if的分支哪个可能性会大一点,先读取可能性大的指令块,尽量避免在发现条件不满足重新读取指令了,但是其实对代码理解没有太大影响,可以忽略它的,它只是告诉计算机,是if还是else的可能性大一点,要预先读取哦。

例如程序是: 
if(I == 1) {
  就去喝水
} else {
  去吃饭
}

如果i很可能不等于1,那么如果计算机先读取了if(i == 1)去喝水,在发现i != 1以后就要重新读取else去吃饭,相当于读了两次。
所以我们提前告诉计算机i很可能不为1的话,可以让计算机一开始先读取else的指令,减少重读可能性

dispatch_queue_create

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);
}

先看看肿么创建一个queue吧~ 第一步先转发给了非常繁琐的_dispatch_lane_create_with_target方法。。
DISPATCH_TARGET_QUEUE_DEFAULT的宏定义为NULL哈

static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
        //其实就是把attr转化为{}字典形式的attr集合,dqai里面会有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;

        …… //设置QOS

    if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
        …… //tq为NULL所以不执行这段
    } else if (tq && !tq->do_targetq) {
        …… //tq为NULL所以不执行这段
    } else {
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            // 串行queue默认overcommit,并行queue默认不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,
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

    // Step 2: Initialize the queue
      
        //如果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) {
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    
        //设置autorelease_frequency以及如果label是muteable的转换为immutable
        ……

    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

        // 设置label,priority,target queue
    dq->dq_label = label;
    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;
}

这里先解释两个名词:
QOS:类似于优先级,在创建global queue的时候的第一个参数就是priority或者QOS
over commit:如果当前队列没有可以分配的空余线程,就开启一个新线程来做任务

创建一个新queue的任务比较重要的几步主要是:
· 设置一下各种参数给dqai
· 用_dispatch_get_root_queue创建一个target queue
· 用_dispatch_object_alloc分配内存,并通过_dispatch_queue_init创建一个queue
· 将各种label、qos等设置给queue
· 调用_dispatch_trace_queue_create返回一个queue

① dqai
#define DISPATCH_QUEUE_SERIAL NULL

dqa在串行队列时为null哦~

dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
    dispatch_queue_attr_info_t dqai = { };

        // dqa如果是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);

    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;

        // 不断做按位取余的方式设置各个参数
    ……

    return dqai;
}

首先如果是串行队列,直接返回空的dqai;
如果是并行队列,则通过按位取余设置下面的各个参数

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;

两种队列是通过dqai_concurrent来区分的

② _dispatch_get_root_queue创建target 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");
    }
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

这里其实是return _dispatch_root_queues[2 * (qos - 1) + overcommit]
也就是说target queue就是从_dispatch_root_queues队列里面拿第(2 * (qos - 1) + overcommit)个queue,由于QOS有6种,并且还有over commit的区分,所以_dispatch_root_queues至少需要有12个queue。

注意串行queue默认是overcommit的,并行是不overcommit的。

// qos
#define DISPATCH_QOS_UNSPECIFIED        ((dispatch_qos_t)0)
#define DISPATCH_QOS_MAINTENANCE        ((dispatch_qos_t)1)
#define DISPATCH_QOS_BACKGROUND         ((dispatch_qos_t)2)
#define DISPATCH_QOS_UTILITY            ((dispatch_qos_t)3)
#define DISPATCH_QOS_DEFAULT            ((dispatch_qos_t)4)
#define DISPATCH_QOS_USER_INITIATED     ((dispatch_qos_t)5)
#define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)
③ 创建一个queue

代码太远了先拷贝过来慢慢看哈~

const void *vtable;
dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
if (dqai.dqai_concurrent) {
    vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
    vtable = DISPATCH_VTABLE(queue_serial);
}

    //设置autorelease_frequency以及如果label是muteable的转换为immutable
    ……

dispatch_lane_t dq = _dispatch_object_alloc(vtable,
        sizeof(struct dispatch_lane_s));
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
        DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
        (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

(1)首先通过了dqai.dqai_concurrent来设置了vtable

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name)   (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class

通过各种宏可以知道其实并行queue的vtable就是OS_dispatch_concurrent_class;串行queue的的vtable就是OS_dispatch_serial_class,这也就是两种queue对应的class。

(2)通过_dispatch_object_alloc给vtable类分配了内存空间,大小为dispatch_lane_s结构体的大小

typedef struct dispatch_lane_s *dispatch_queue_serial_t;
typedef struct dispatch_lane_s *dispatch_queue_concurrent_t;

(3)通过_dispatch_queue_init初始化queue
这一段主要是设置state、width之类的

static inline dispatch_queue_class_t
_dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf,
        uint16_t width, uint64_t initial_state_bits)
{
    uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);
    dispatch_queue_t dq = dqu._dq;

    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->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume
        if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
            dq->do_ref_cnt++; // released when DSF_DELETED is set
        }
    }

    dq_state |= (initial_state_bits & DISPATCH_QUEUE_ROLE_MASK);
    dq->do_next = DISPATCH_OBJECT_LISTLESS;
    dqf |= DQF_WIDTH(width);
    os_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);
    return dqu;
}

两个小概念哈:
· width:最大并发的线程数

#define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2)  //4094
#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull 

· initial_state:这个queue是不是一开始就是active状态,如果不是的话,需要手动执行dispatch_active以后才能激活

// inactive的initial_state_bits如下,如果是一开始就active的initial_state_bits为0
#define DISPATCH_QUEUE_INACTIVE             0x0100000000000000ull

(4)设置priority、label、target queue
(5)返回_dispatch_trace_queue_create(queue)
将队列插入到队列数组中,设置队列头尾节点等

_dispatch_trace_queue_create
    |___dispatch_introspection_queue_create

现在我们create两个queue看看它们的属性吧~

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

Printing description of serialQueue:
<OS_dispatch_queue_serial: serial_queue[0x6000024caa00] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos.overcommit[0x10d875f00], width = 0x1, state = 0x001ffe2000000000, in-flight = 0}>

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

Printing description of concurrentQueue:
<OS_dispatch_queue_concurrent: concurrent_queue[0x6000024ffb80] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos[0x10d875e80], width = 0xffe, state = 0x0000041000000000, in-flight = 0}>

可以看到它们的class, width, target queue都和我们源码看的一致~

_dispatch_root_queues

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,
    ),
};

我们创建串行queue的时候是取的第2 * (qos - 1) + overcommit = 2 * (DISPATCH_QOS_DEFAULT - 1) + 1 = 2 * (4 - 1) + 1 = 7个queue,也就是"com.apple.root.default-qos.overcommit",和上面试验创建串行queue拿到的target queue是一致的。

注意哦,root queue的list的确有12个queue,但编号是从4开始的哈~

dispatch_get_main_queue

我们再来看一下主队列是如何获取的:

dispatch_queue_main_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}

#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))

---------------------------------------
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,
};

---------------target queue-------------
#define _dispatch_get_default_queue(overcommit) \
        _dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS + \
                !!(overcommit)]._as_dq


enum {
    DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS = 0,
    DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_OVERCOMMIT,
    DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS,
    DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_OVERCOMMIT,
    DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS,
    DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_OVERCOMMIT,
    DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS,
    DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT,
    DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS,
    DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_OVERCOMMIT,
    DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS,
    DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_OVERCOMMIT,
    _DISPATCH_ROOT_QUEUE_IDX_COUNT,
};

由于DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS=6, overcommit为true,所以main_queue拿的是_dispatch_root_queues里面的第七个,也就是"com.apple.root.default-qos.overcommit",和串行queue拿到的target queue是一致的。

_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_get_global_queue

dispatch_queue_global_t
dispatch_get_global_queue(long priority, unsigned long flags)
{
    dispatch_assert(countof(_dispatch_root_queues) ==
            DISPATCH_ROOT_QUEUE_COUNT);

    if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
        return DISPATCH_BAD_INPUT;
    }
    dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == QOS_CLASS_MAINTENANCE) {
        qos = DISPATCH_QOS_BACKGROUND;
    } else if (qos == QOS_CLASS_USER_INTERACTIVE) {
        qos = DISPATCH_QOS_USER_INITIATED;
    }
#endif
    if (qos == DISPATCH_QOS_UNSPECIFIED) {
        return DISPATCH_BAD_INPUT;
    }
    return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}

获取global对列的最后也是转调的_dispatch_get_root_queue,返回_dispatch_root_queues[2 * (qos - 1) + overcommit],在这个场景下的QOS为DISPATCH_QOS_DEFAULT=4(对应priority为0的时候),默认overcommit也是0,所以拿的是第6个queue,也就是"com.apple.root.default-qos"。

_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
        .dq_label = "com.apple.root.default-qos",
        .dq_serialnum = 10,
    ),

#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__ \
    }

#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1) //4095

总结一下各种queue的获取方式吧:

  1. 自己create创建的queue是需要alloc分配内存以后init,最后从root_queue里面的拿一个作为新queue的target queue的
  2. main和global queue是不需要alloc init的,直接从root_queue里拿出对应的queue即可

dispatch_async

dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME;
    dispatch_qos_t qos;

    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

先来看一下dispatch_continuation_t是神马~ 通过各种转调,大概就是一个结构体,用于封装block的

typedef struct dispatch_continuation_s *dispatch_continuation_t;

typedef struct dispatch_continuation_s {
    DISPATCH_CONTINUATION_HEADER(continuation);
} *dispatch_continuation_t;

#define DISPATCH_CONTINUATION_HEADER(x) \
    dispatch_function_t dc_func; \
    union { \
        pthread_priority_t dc_priority; \
        int dc_cache_cnt; \
        uintptr_t dc_pad; \
    }; \
    struct voucher_s *dc_voucher; \
    union { \
        const void *do_vtable; \
        uintptr_t dc_flags; \
    }; \
    struct dispatch_##x##_s *volatile do_next; \
    void *dc_ctxt; \
    void *dc_data; \
    void *dc_other

通过_dispatch_continuation_init将dc结构体里面的各个参数设好。

_dispatch_continuation_init
      |___dispatch_continuation_init_f
                 |____dispatch_continuation_priority_set

static inline dispatch_qos_t
_dispatch_continuation_init(dispatch_continuation_t dc,
        dispatch_queue_class_t dqu, dispatch_block_t work,
        dispatch_block_flags_t flags, uintptr_t dc_flags)
{
    void *ctxt = _dispatch_Block_copy(work);

    dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
    if (unlikely(_dispatch_block_has_private_data(work))) {
        dc->dc_flags = dc_flags;
        dc->dc_ctxt = ctxt;
        // will initialize all fields but requires dc_flags & dc_ctxt to be set
        return _dispatch_continuation_init_slow(dc, dqu, flags);
    }

    dispatch_function_t func = _dispatch_Block_invoke(work);
    if (dc_flags & DC_FLAG_CONSUME) {
        func = _dispatch_call_block_and_release;
    }
    return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}

void
_dispatch_call_block_and_release(void *block)
{
    void (^b)(void) = block;
    b();
    Block_release(b);
}

并且如果是consume flag会直接执行block滴。然后我们接着看下面的。

static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
        dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
    if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
        _dispatch_trace_item_push(dqu, dc);
    }
#else
    (void)dc_flags;
#endif
    return dx_push(dqu._dq, dc, qos);
}

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

可以看出依次调用了dx_push以及dq_push,那么dq_push是什么呢又?

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_concurrent_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_root_queue_wakeup,
    .dq_push        = _dispatch_root_queue_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_main_queue_wakeup,
    .dq_push        = _dispatch_main_queue_push,
);

这里以concurrent queue的dq_push为例看一下它做了什么:

_dispatch_lane_concurrent_push(dispatch_lane_t dq, dispatch_object_t dou,
        dispatch_qos_t qos)
{
    if (dq->dq_items_tail == NULL &&
            !_dispatch_object_is_waiter(dou) &&
            !_dispatch_object_is_barrier(dou) &&
            _dispatch_queue_try_acquire_async(dq)) {
        return _dispatch_continuation_redirect_push(dq, dou, qos);
    }

    _dispatch_lane_push(dq, dou, qos);
}

static void
_dispatch_continuation_redirect_push(dispatch_lane_t dl,
        dispatch_object_t dou, dispatch_qos_t qos)
{
    if (likely(!_dispatch_object_is_redirection(dou))) {
        dou._dc = _dispatch_async_redirect_wrap(dl, dou);
    } else if (!dou._dc->dc_ctxt) {
        // find first queue in descending target queue order that has
        // an autorelease frequency set, and use that as the frequency for
        // this continuation.
        dou._dc->dc_ctxt = (void *)
        (uintptr_t)_dispatch_queue_autorelease_frequency(dl);
    }

    dispatch_queue_t dq = dl->do_targetq;
    if (!qos) qos = _dispatch_priority_qos(dq->dq_priority);
    dx_push(dq, dou, qos);
}

DISPATCH_ALWAYS_INLINE
static inline dispatch_continuation_t
_dispatch_async_redirect_wrap(dispatch_lane_t dq, dispatch_object_t dou)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();

    dou._do->do_next = NULL;
    dc->do_vtable = DC_VTABLE(ASYNC_REDIRECT);
    dc->dc_func = NULL;
    dc->dc_ctxt = (void *)(uintptr_t)_dispatch_queue_autorelease_frequency(dq);
    dc->dc_data = dq;
    dc->dc_other = dou._do;
    dc->dc_voucher = DISPATCH_NO_VOUCHER;
    dc->dc_priority = DISPATCH_NO_PRIORITY;
    _dispatch_retain_2(dq); // released in _dispatch_async_redirect_invoke
    return dc;
}

void
_dispatch_lane_push(dispatch_lane_t dq, dispatch_object_t dou,
        dispatch_qos_t qos)
{
    dispatch_wakeup_flags_t flags = 0;
    struct dispatch_object_s *prev;

    if (unlikely(_dispatch_object_is_waiter(dou))) {
        return _dispatch_lane_push_waiter(dq, dou._dsc, qos);
    }

    dispatch_assert(!_dispatch_object_is_global(dq));
    qos = _dispatch_queue_push_qos(dq, qos);

    prev = os_mpsc_push_update_tail(os_mpsc(dq, dq_items), dou._do, do_next);
    if (unlikely(os_mpsc_push_was_empty(prev))) {
        _dispatch_retain_2_unsafe(dq);
        flags = DISPATCH_WAKEUP_CONSUME_2 | DISPATCH_WAKEUP_MAKE_DIRTY;
    } else if (unlikely(_dispatch_queue_need_override(dq, qos))) {
        _dispatch_retain_2_unsafe(dq);
        flags = DISPATCH_WAKEUP_CONSUME_2;
    }
    os_mpsc_push_update_prev(os_mpsc(dq, dq_items), prev, dou._do, do_next);
    if (flags) {
        return dx_wakeup(dq, qos, flags);
    }
}

大概流程就是当队尾为NULL是,就将任务转发给target queue(一般为root queue列表中的某一个,计算方法见之前的公式)。在_dispatch_lane_push里面会把队列最后一个的next指向当前任务(os_mpsc_push_update_prev),就类似一个链表。

所以其实大部分情况,我们创建的queue的任务其实最后都是转发给了target queue。target queue就是root queue,所以来看一下root queue的dx_push:

_dispatch_root_queue_push
       |____dispatch_root_queue_push_inline
                      |____dispatch_root_queue_poke (仅第一个任务的时候激活调用)
                                    |____dispatch_root_queue_poke_slow

_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
    int remaining = n;
    int r = ENOSYS;

    _dispatch_root_queues_init();
    _dispatch_debug_root_queue(dq, __func__);
    _dispatch_trace_runtime_event(worker_request, dq, (uint64_t)n);

#if !DISPATCH_USE_INTERNAL_WORKQUEUE
#if DISPATCH_USE_PTHREAD_ROOT_QUEUES
    if (dx_type(dq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE)
#endif
    {
        _dispatch_root_queue_debug("requesting new worker thread for global "
                "queue: %p", dq);
        r = _pthread_workqueue_addthreads(remaining,
                _dispatch_priority_to_pp_prefer_fallback(dq->dq_priority));
        (void)dispatch_assume_zero(r);
        return;
    }
#endif // !DISPATCH_USE_INTERNAL_WORKQUEUE
#if DISPATCH_USE_PTHREAD_POOL
    ……
}

第一次激活的时候其实是在poke_slow里面通过_pthread_workqueue_addthreads之类的进行线程分配增加的。

现在我们来尝试打印一下async里面任务的执行栈:

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

dispatch_async(concurrentQueue, ^{
    NSLog(@"%@",[NSThread callStackSymbols]);
});

输出:
(
    0   Example1                            0x000000010410380a __16-[TestGCD test1]_block_invoke_9 + 74
    1   libdispatch.dylib                   0x0000000106cafd7f _dispatch_call_block_and_release + 12
    2   libdispatch.dylib                   0x0000000106cb0db5 _dispatch_client_callout + 8
    3   libdispatch.dylib                   0x0000000106cb3c95 _dispatch_continuation_pop + 552
    4   libdispatch.dylib                   0x0000000106cb308f _dispatch_async_redirect_invoke + 849
    5   libdispatch.dylib                   0x0000000106cc1632 _dispatch_root_queue_drain + 351
    6   libdispatch.dylib                   0x0000000106cc1fca _dispatch_worker_thread2 + 130
    7   libsystem_pthread.dylib             0x00000001070996b3 _pthread_wqthread + 583
    8   libsystem_pthread.dylib             0x00000001070993fd start_wqthread + 13
)

可以看出其实任务的调用栈其实起始于start_wqthread,但是这个是汇编代码没法看到,所以从_dispatch_worker_thread2开始向上追溯,看看是如何初始化这个worker的。

_dispatch_root_queue_poke_slow
   |__ _dispatch_root_queues_init
      |__ _dispatch_root_queues_init_once
         |__ _pthread_workqueue_init_with_kevent(_dispatch_worker_thread2……)

总结一下dispatch_async在第一次初始化会调用_dispatch_root_queue_poke_slow,这个时候init了worker,之后的任务会通过worker调度执行。

Reference:
超级赞的源码分析!https://bestswifter.com/deep-gcd/
源码分析:https://www.jianshu.com/p/7702c06cda4c
https://www.jianshu.com/p/bd629d25dc2e

相关文章

网友评论

      本文标题:[iOS] GCD是神马-源码分析

      本文链接:https://www.haomeiwen.com/subject/weolsctx.html