GCD源码解析(一)-dispatch_queue_create、dispatch_get_main_queue、dispatch_get_global_queue

阅读GCD源码,首先需要下载libdispatch源码。很多同学拿到源码就直接埋头苦干,我觉得这样会像无头苍蝇一样没有重点,容易懵逼,效率低。相比objc,CFRunloop源码而言,libdispatch源码难度比较高。libdispatch源码中使用了很多宏、且名字特别长、嵌套深、还包含很多os开头的函数,所以阅读起来不是那么容易。
本文以问题的形式来窥探源码,这种方式比较有重点,效率高。

使用GCD的队列需从创建队列开始,因此我们源码分析也从队列的创建开始分析。

 dispatch_queue_create("jensen.cn", NULL);

对于队列的创建,我们先探索如下2个问题:
1.队列是如何产生的?
2.串行队列与并行队列有什么区别?

带着这两个问题,我们打开源码找到dispatch_queue_create函数:

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

dispatch_queue_create函数的attr一般传入DISPATCH_QUEUE_SERIALDISPATCH_QUEUE_CONCURRENT或者nil,而宏定义DISPATCH_TARGET_QUEUE_DEFAULT其实就是null
进入_dispatch_lane_create_with_target,这个函数就是创建队列的核心函数。

DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
       //通过当前的属性获取属性信息
    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 来区分并发和串行
        // 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;
        }
    }

    // 开辟内存 - 生成响应的对象 queue
    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));

    // 标签
    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;

}

代码中首先通过外部传入的dqa属性获取属性信息dqai。进入获取属性信息的函数_dispatch_queue_attr_to_info

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

      //串行队列,返回空
    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);

    // 位域写法  通过idx不断的获取对应的值通过取余
    // 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;
}

如果是参数dqa为NULL,_dispatch_queue_attr_to_info函数直接返回dispatch_queue_attr_info_t类型的空结构体。如果不是NULL,苹果将通过规定的算法计算出idx,idx采用了位域写法,通过取余方式获取对应的值,为dqa初始化。
dispatch_queue_attr_info_t类型如下

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函数,我们发现并行和串行是通过dqai.diqi_concurrent来区分并行队列和串行队列的。

if (dqai.dqai_concurrent) {
        // 通过dqai.dqai_concurrent 来区分并发和串行
        // OS_dispatch_queue_concurrent_class
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }

查看代码中DISPATCH_VTABLE的定义:

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

OS_dispatch_##name##_class,底层将传入的参数name,拼接生成对应的队列类,
串行队列的类为: OS_dispatch_queue_serial_class,并行队列的类为:OS_dispatch_queue_concurrent_class

    if (label) {
        const char *tmp = _dispatch_strdup_if_mutable(label);
        if (tmp != label) {
            dqf |= DQF_LABEL_NEEDS_FREE;
            label = tmp;
        }
    }
    // 开辟内存 - 生成响应的对象 queue
    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));

    // 标签
    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;

判断参数label如果不为NULL,调用函数_dispatch_strdup_if_mutable(const char *str)进行处理,判断如果源字符串是可变的,就重新分配内存并拷贝内容到内存中。否则直接返回。

const char *
_dispatch_strdup_if_mutable(const char *str)
{
#if HAVE_DYLD_IS_MEMORY_IMMUTABLE
    size_t size = strlen(str) + 1;
    if (unlikely(!_dyld_is_memory_immutable(str, size))) {
        char *clone = (char *) malloc(size);
        if (dispatch_assume(clone)) {
            memcpy(clone, str, size);
        }
        return clone;
    }
    return str;
#else
    return strdup(str);
#endif
}

处理完成后,判断是否是原来的那一个label,不一致就替换。
接下来调用_dispatch_object_alloc函数为队列分配内存,生成响应的对象dq,将队列包装成dispatch_lane_t类型。
然后调用_dispatch_queue_init构造函数,为队列初始化。构造方法的参数width

#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)
    // 构造方法
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

通过dqai.dqai_concurrent判断,如果是并行队列,width0xffe,十进制为4094,如果是串行队列则width为1,通过这里我们也就知道为什么并行队列口子大,串行队列口子小。
再为dq的所有属性赋值,dq_labeldq_priority等等。
将自己创建的队列绑定到root队列上。

我们注意到在_dispatch_lane_create_with_target函数在最后调用了_dispatch_trace_queue_create函数,在_dispatch_trace_queue_create函数中调用_dispatch_introspection_queue_create函数,在_dispatch_introspection_queue_create中调用_dispatch_introspection_queue_create_hook函数,在_dispatch_introspection_queue_create_hook函数调用dispatch_introspection_queue_get_info函数。

#define dx_vtable(x) (&(x)->do_vtable->_os_obj_vtable)
#define dx_metatype(x) (dx_vtable(x)->do_type & _DISPATCH_META_TYPE_MASK)
DISPATCH_USED inline
dispatch_introspection_queue_s
dispatch_introspection_queue_get_info(dispatch_queue_t dq)
{
    if (dx_metatype(dq) == _DISPATCH_WORKLOOP_TYPE) {
        return _dispatch_introspection_workloop_get_info(upcast(dq)._dwl);
    }
    return _dispatch_introspection_lane_get_info(upcast(dq)._dl);
}

通过dx_metatype判断是否为并发队列,如果为串行队列则调用_dispatch_introspection_workloop_get_info函数,否则调用_dispatch_introspection_lane_get_info函数。

static inline dispatch_introspection_queue_s
_dispatch_introspection_workloop_get_info(dispatch_workloop_t dwl)
{
    uint64_t dq_state = os_atomic_load2o(dwl, dq_state, relaxed);

    dispatch_introspection_queue_s diq = {
        .queue = dwl->_as_dq,
        .target_queue = dwl->do_targetq,
        .label = dwl->dq_label,
        .serialnum = dwl->dq_serialnum,
        .width = 1,
        .suspend_count = 0,
        .enqueued = _dq_state_is_enqueued(dq_state),
        .barrier = _dq_state_is_in_barrier(dq_state),
        .draining = 0,
        .global = 0,
        .main = 0,
    };
    return diq;
}

static inline dispatch_introspection_queue_s
_dispatch_introspection_lane_get_info(dispatch_lane_class_t dqu)
{
    dispatch_lane_t dq = dqu._dl;
    bool global = _dispatch_object_is_global(dq);
    uint64_t dq_state = os_atomic_load2o(dq, dq_state, relaxed);

    dispatch_introspection_queue_s diq = {
        .queue = dq->_as_dq,
        .target_queue = dq->do_targetq,
        .label = dq->dq_label,
        .serialnum = dq->dq_serialnum,
        .width = dq->dq_width,
        .suspend_count = _dq_state_suspend_cnt(dq_state) + dq->dq_side_suspend_cnt,
        .enqueued = _dq_state_is_enqueued(dq_state) && !global,
        .barrier = _dq_state_is_in_barrier(dq_state) && !global,
        .draining = (dq->dq_items_head == (void*)~0ul) ||
                (!dq->dq_items_head && dq->dq_items_tail),
        .global = global,
        .main = dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE,
    };
    return diq;
}

函数中将传入的队列用dispatch_introspection_queue_s这个类型重新包装,重新赋值。

typedef struct dispatch_introspection_queue_s {
    dispatch_queue_t queue;
    dispatch_queue_t target_queue;
    const char *label;
    unsigned long serialnum;
    unsigned int width;
    unsigned int suspend_count;
    unsigned long enqueued:1,
            barrier:1,
            draining:1,
            global:1,
            main:1;
} dispatch_introspection_queue_s;

至此,我们已经了解队列创建的整个过程,以及代码中是如何区别串行队列和并行队列。下面我们来验证一下:

  dispatch_queue_t queueSer = dispatch_queue_create("com.jensen.ser", NULL);
    dispatch_queue_t queueCon = dispatch_queue_create("com.jensen.con", DISPATCH_QUEUE_CONCURRENT);

通过lldb,我们分别打印queueSer和queueCon队列:


7C8B2DEC5F3093B133073C7A861CF8E7.png

打印结果中,我们看到串行队列和并行队列的类分别是OS_dispatch_queue_serialOS_dispatch_queue_concurrent,label分别是调用时传入的参数com.jensen.sercom.jensen.con,width分别为0x10xffe,与我们代码分析的结果一致。

但是我们还是有疑问,串行队列和并行队列的target分别为com.apple.root.default-qos.overcommitcom.apple.root.default-qos,这个是如何区别并关联的呢?带着这个问题我们继续分析。
其实这个target跟我们上述提到的,将自己创建的队列绑定到root队列有关系。

dq->do_targetq = tq;

那么我们要弄清楚target, 那么就必须从获取root队列的函数入手。

#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)
#define DISPATCH_QOS_MIN                DISPATCH_QOS_MAINTENANCE
#define DISPATCH_QOS_MAX                DISPATCH_QOS_USER_INTERACTIVE
#define DISPATCH_QOS_SATURATED          ((dispatch_qos_t)15)

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

往上查看发现dispatch_qos_t qos = dqai.dqai_qos 代码, 那么我们得出当队列是串行队列qosDISPATCH_QOS_UNSPECIFIED_dispatch_get_root_queue函数传入的第一个参数为DISPATCH_QOS_DEFAULT,否则为qos本身。第二个参数为overcommit == _dispatch_queue_attr_overcommit_enabled,是一个bool,要那么是0,要么是1。跟进_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];
}

当为串行队列时,qos4,overcommit0/1,2 * (qos - 1) + overcommit 的下标为2*(4-1)+0/1 = 6/7.

我们找到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,
    ),
};

通过查看dispatch_root_queues静态数组的初始化,我们知道target queue其实 是一些已经确定的值。那么上述得出的下标6/7,在dispatch_root_queues静态数组可查到target queuelabelcom.apple.root.default-qos或者com.apple.root.default-qos.overcommit

我们已经了解队列创建时,root target是如何关联的了。我们联想到系统默认帮我们创建的主队列dispatch_get_main_queue()和全局并发队列dispatch_get_global_queue(0, 0)又是怎么样的呢?
带着问题,我们先通过lldb打印主队列信息和全局队列的信息。

    dispatch_queue_t queueMain = dispatch_get_main_queue();
    dispatch_queue_t queueglob = dispatch_get_global_queue(0, 0);
662096559D60EB2E5851D5605C8222AA.png

进入dispatch_get_main_queue()dispatch_get_main_queue()源码,

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

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

dispatch_queue_main_tdispatch_queue_static_s类型的结构体指针。

typedef struct dispatch_queue_static_s *dispatch_queue_main_t;

我们可以查看dispatch_queue_main_t如何创建的

DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial);
#define DISPATCH_DECL_SUBCLASS(name, base) OS_OBJECT_DECL_SUBCLASS(name, base)
#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, <OS_OBJECT_CLASS(super)>) 
#define OS_OBJECT_DECL_IMPL(name, ...) \
        OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
        typedef NSObject<OS_OBJECT_CLASS(name)> \
                * OS_OBJC_INDEPENDENT_CLASS name##_t

从上述得知,dispatch_queue_main是继承dispatch_queue_serial, 并重写dispatch_queue_serial的函数。这就追溯到我们最开始的串行队列创建。
这里传入dispatch_queue_main,所以主队列的底层类就是OS_dispatch_queue_main_class,与我们的打印也是一致的。
_dispatch_main_q的初始化如下:

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

_dispatch_main_q的初始化中,我们可以看到主队列的labelcom.apple.main-thread,这就可以说明为什么主队列的labelcom.apple.main-thread
上述代码中可以看到主队列的targetq_dispatch_get_default_queue(true),

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

可以看到主队列的target是直接从静态数组_dispatch_root_\queues取值的。

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_QOS6,因此下标为7,查找静态数组得知下标为7target的``label为com.apple.root.default-qos.overcommit,与我们例子打印出的一致。
我们已经弄清楚了主队列是怎么回事了。接下来我们继续分析全局队列dispatch_get_global_queue(0, 0)

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

dispatch_get_global_queue(long priority, unsigned long flags)函数中调用_dispatch_qos_from_queue_priority(long priority),通过priority获取qos

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QOS_DEFAULT            ((dispatch_qos_t)4)
DISPATCH_ALWAYS_INLINE
static inline dispatch_qos_t
_dispatch_qos_from_queue_priority(long priority)
{
    switch (priority) {
    case DISPATCH_QUEUE_PRIORITY_BACKGROUND:      return DISPATCH_QOS_BACKGROUND;
    case DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE: return DISPATCH_QOS_UTILITY;
    case DISPATCH_QUEUE_PRIORITY_LOW:             return DISPATCH_QOS_UTILITY;
    case DISPATCH_QUEUE_PRIORITY_DEFAULT:         return DISPATCH_QOS_DEFAULT;
    case DISPATCH_QUEUE_PRIORITY_HIGH:            return DISPATCH_QOS_USER_INITIATED;
    default: return _dispatch_qos_from_qos_class((qos_class_t)priority);
    }
}

例子中输入的priority0,得出qos4.
最后调用函数_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)直接获得队列。

_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];
}

通过静态数组找到下标为6,就是全局并发队列的label

#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#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__ \
    }

通过上述我们可以知道全局并发队列的widthDISPATCH_QUEUE_WIDTH_FULL - 1,0xfff,比我们自己创建的自定义队列0xffe1
通过上述分析我们也总结出全局并发队列和自定义并发队列的区别在于:
全局并发队列直接从静态数组中取出,而自定义全局队列需手动alloc,并关联root队列。全局并发队列width0xfff,而自定义并发队列的width0ffe

总结:本文通过问题的方式简要探索gcd关于队列创建函数dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr),主队列dispatch_get_main_queue(void),全局并发队列dispatch_get_global_queue(long identifier, unsigned long flags)的源码。gcd源码难度较大,本位仅仅只是对一些要点进行指出。本文中可能存在说明不妥当的地方,希望大牛们多多指教!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 原文链接深入理解GCD之dispatch_queue[https://www.neroxie.com/2019/0...
    NeroXie阅读 9,340评论 8 37
  • Grend Central Dispatch(GCD)调度队列是执行任务的强大工具。调度队列让我们可以与调用者异步...
    渐z阅读 1,019评论 0 0
  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,731评论 1 17
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,085评论 1 32
  • 在计算机的早期,中央处理器(Central Processing Unit,简称CPU)的主频(即时钟频率, cl...
    pro648阅读 1,274评论 0 4