iOS底层探索-----GCD底层分析 下

560 阅读12分钟

前言

上篇文章对 GCDOC 层的使用做了详细的探索,那么这篇文章将对 GCD 的底层进行探索。

资源准备

主队列的底层分析

主队列是GCD提供的特殊的串行队列,在libdispatch源码中,找到主队列的定义,找到它是串行队列的依据

主队列的实现类型

有两种实现类型:

类型一

通过 dispatch_get_main_queue 这种方法实现,我们可以在libdispatch源码中进行搜索,源码如下:

CC818BA2-A4DF-4BC4-80C7-47461F950514.png

其中参数1dispatch_queue_main_t,这个是个结构体。

B7D217AF-2EE1-4E74-8E34-B3937E5BC891.png

dispatch_queue_static_s 结构体的实现如下:

C50B61D7-C33B-43CE-AEB0-0F8357CAB4D1.png

参数2_dispatch_main_q为队列类型,在源码中搜索_dispatch_main_q,找到的结果非常多,这时可以尝试搜索_dispatch_main_q =,如果是赋值操作,可以缩小结果的范围,通过搜索,看底层实现:

ACB3529C-3740-439C-8754-50CDC347121E.png

类型二

9604B729-BF95-4728-94D9-D2F6387AC34A.png

在代码中,创建主队列,使用lldb,执行命令 po queue, 可以输出它的结构,其中包含队列标识符。打印出结果后,可以知道和主队列关联的com.apple.main-thread,可以把这个作为关键词在libdispatch源码中进行搜索。就能直接找到上图_dispatch_main_q =的结果。

主队列是串行队列的探索

在刚刚主队列的底层探索中,可以知道,主队列的dq_atomic_flagsDQF_THREAD_BOUND | DQF_WIDTH(1);那么我们可以通过找到串行队列的dq_atomic_flags与主队列的进行对比,如果一致,即可证明主队列就是一个串行队列。

创建串行队列,使用的是:dispatch_queue_create,那么在libdispatch源码中搜索这个关键词:

773CE290-2654-4DCF-B915-332994A4983B.png

其中返回的是 _dispatch_lane_create_with_target 函数,那么接着进入这个函数里面,查看实现情况:

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) 
// 前期的准备工作,优先级处理 
...

// Step 2: Initialize the 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); 
} 

// 申请开辟内存 
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; 
return _dispatch_trace_queue_create(dq)._dq; 
}
  • 通过_dispatch_queue_attr_to_infodqa进行面向对象的包装,为dqai

  • _dispatch_queue_init是构造函数,看它的第三个参数,当是concurrent的时候传DISPATCH_QUEUE_WIDTH_MAX,否则传1

进入_dispatch_queue_init函数,找到参数3:

BBA4220F-8532-4EB1-A849-9011B1E2D51F.png

如果是串行队列,传入的width1dqfDQF_WIDTH(1),和主队列dq_atomic_flags属性设置的值一致,所以主队列就是一个串行队列。

在这个函数里面,还有 dq_serialnum 这参数,是用来做什么的了?

dq_serialnum的作用

有一些说法,如果dq_serialnum设置为1,就是串行队列,例如:主队列的设置

libdispatch源码中,搜索dq_serialnum,可以找到很多赋值的场景

CD0C5B07-C276-4A5C-81B1-A45C3BB870BB.png

查看 DISPATCH_QUEUE_SERIAL_NUMBER_WLF 的定义:

60FF1EBB-8988-4A44-82CD-F2FFF56F3867.png

同时找到了dq_serialnum的相关注释

  • 跳过零

  • 主队列:1

  • mgr_q2

  • mgr_root_q3

  • 全局队列:4~15

  • 自定义队列:17

所以dq_serialnum并不是用来标记串行和并发队列的。

自定义队列的dq_serialnum

从上面的分析,我们知道自定义队列是 17

#define DISPATCH_QUEUE_SERIAL_NUMBER_INIT 17

搜索DISPATCH_QUEUE_SERIAL_NUMBER_INIT,找到被赋值的代码:

494686AC-9ED9-4C8A-A70D-A83D76A9EF19.png

那么就接着探索 _dispatch_queue_serial_numbers

1F15B7B2-E550-4254-8C43-9781DF45899F.png

再探索下 os_atomic_inc_orig 这个宏定义:

#define os_atomic_inc_orig(p, m) \ 
        os_atomic_add_orig((p), 1, m) 

#define os_atomic_add_orig(p, v, m) \ 
        _os_atomic_c11_op_orig((p), (v), m, add, +) 

#define _os_atomic_c11_op_orig(p, v, m, o, op) \
        atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), v, \ 
        memory_order_##m)
  • atomic_fetch_##o##_explicit中的##o##为占位,替换的o传入的值为add

  • 替换后为atomic_fetch_add_explicit,来自C++11的原子操作函数

  • 宏的作用,传入17,之后每次+1

全局队列分析

我们先探究下,我们在使用全局队列时,能通过 lldb 打印出什么?

84CC5E32-F708-4425-ABC9-4FB982A7923C.png

打印出结果之后,我们就可以在libdispatch源码里面进行探索了,以 com.apple.root.default-qos 为关键词搜索:

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

    //✅  com.apple.root.default-qos
    _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为数组类型,默认将4~15的全局队列全部初始化,获取时通过不同类型,获取不同的队列。

  • 和主队列的类型差异:

    • 主队列:dispatch_queue_static_s
    • 全局队列:dispatch_queue_global_s

创建队列

搜索dispatch_queue_create,进入_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) { 
   
    // 1、创建dqai 
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa); 

    // 2、规范化参数,例如:qos, overcommit, tq 
    dispatch_qos_t qos = dqai.dqai_qos;
    ... 

    // 3、根据队列类型,创建vtable 
    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) { 
        vtable = DISPATCH_VTABLE(queue_concurrent); 
    } else { 
        vtable = DISPATCH_VTABLE(queue_serial); 
    } 
    ... 

    // 4、开辟空间,初始化队列 
    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; 
    ... 

    // 5、根据不同类型,通过模板创建队列
    dq->do_targetq = tq; 
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq; 
}

创建dqai

在源码里面搜索_dispatch_queue_attr_to_info,并且进入该函数:

C517C911-07E9-449F-BCB9-F25A9F274715.png

  • 创建空的dqai,默认为串行队列。如果传入的参数为空,返回空dqai

  • 传入的参数有值,将队列信息保存到dqai

规范化参数

规范化参数,例如:qos, overcommit, tq ...

6D721908-82A7-40E7-9D34-584BD52D78D9.png

  • 设置优先级、服务质量等参数。

创建vtable

根据队列类型,创建 vtable 。 通过DISPATCH_VTABLE,将传入的队列类型拼接vtable

084D1966-4A41-46D2-BBE6-688A8767C0FA.png

再查看下DISPATCH_VTABLE的宏定义:

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)

#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name)) 

#define DISPATCH_CLASS_SYMBOL(name) _dispatch_##name##_vtable

初始化队列

①、使用_dispatch_object_alloc函数开辟空间:

7B820179-46ED-47FD-8DAD-C0FD121BF2C7.png

再进入_os_object_alloc_realized函数:

BA10C895-8AF1-4F01-9F00-376ECBF33E69.png

  • 包含isa指向,说明队列也是一个对象。

②、使用_dispatch_queue_init函数初始化队列,进入函数里面查看其实现:

A33C4065-1395-4053-844E-CBFB951AC367.png

  • 队列类型为dispatch_queue_t,为队列的成员变量赋值.

通过模板创建队列

使用_dispatch_introspection_queue_create函数,传入初始化后的dq

流程:_dispatch_introspection_queue_create_dispatch_introspection_queue_create_hookdispatch_introspection_queue_get_info_dispatch_introspection_lane_get_info

BA09CEDA-D1E8-4D85-9CEB-4657112ED45E.png

继承链

使用_dispatch_lane_create_with_target创建队列,返回dispatch_queue_t类型。

dispatch_queue_t类型来自于宏定义

DISPATCH_DECL(dispatch_queue); 

#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)

#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, NSObject, <OS_OBJECT_CLASS(super)>) 

#define OS_OBJECT_DECL_IMPL(name, adhere, ...) \ 
        OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \ 
        typedef adhere<OS_OBJECT_CLASS(name)> \ * OS_OBJC_INDEPENDENT_CLASS name##_t 

#define OS_OBJECT_DECL_PROTOCOL(name, ...) \ 
        @protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \ @end 

#define OS_OBJECT_CLASS(name) OS_##name
  • 第一步:使用OS_OBJECT_CLASS在前面拼接OS_,例如:OS_dispatch_queue

  • 第二步:使用OS_OBJECT_DECL_PROTOCOL在结尾拼接_t,例如:OS_dispatch_queue_t

  • 第三步:使用泛型接收,typedef adhere<OS_dispatch_queue> * OS_OBJC_INDEPENDENT_CLASS OS_dispatch_queue_t

DISPATCH_DECL宏在dispatch_object_s结构体中的定义:

B72CA4E2-E2EF-464F-94B1-2AEBD7528484.png

  • 那么把参数带入进去,就能得到: typedef struct dispatch_queue_s : public dispatch_object_s {} *dispatch_queue_t

  • 相当于: dispatch_queue_t的本质是dispatch_queue_s,而dispatch_queue_s继承自dispatch_object_s

dispatch_object_s来自于dispatch_object_t的联合体:

210D1438-F47E-48A6-89D0-ADF379254733.png

  • dispatch_object_t可以代表联合体里面的所有的类型,所以最终的根类是dispatch_object_t

  • 最终得到的继承链:dispatch_queue_t --> dispatch_queue_s --> dispatch_object_s --> dispatch_object_t

现在我们探究的是dispatch_queue_t,所以要看其本质,也就是dispatch_queue_s。找到dispatch_queue_s的结构:

85F63801-CA83-4B1D-AFC6-CD5BBE1CA940.png

再看下结构体内嵌套的DISPATCH_QUEUE_CLASS_HEADER定义:

#define DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__); \

    /* LP64 global queue cacheline boundary */ \
    unsigned long dq_serialnum; \
    const char *dq_label; \
    DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \
    const uint16_t dq_width, \
    const uint16_t __dq_opaque2 \
    ); \
    dispatch_priority_t dq_priority; \
    union { \
        struct dispatch_queue_specific_head_s *dq_specific_head; \
        struct dispatch_source_refs_s *ds_refs; \
        struct dispatch_timer_source_refs_s *ds_timer_refs; \
        struct dispatch_mach_recv_refs_s *dm_recv_refs; \
        struct dispatch_channel_callbacks_s const *dch_callbacks; \
    };\

int volatile dq_sref_cnt

struct dispatch_queue_s {
    DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;

struct dispatch_workloop_s {
    struct dispatch_queue_s _as_dq[0];
    DISPATCH_QUEUE_CLASS_HEADER(workloop, dispatch_timer_heap_t dwl_timer_heap);
    uint8_t dwl_drained_qos;

    /* 24 bits hole */
    struct dispatch_object_s *dwl_heads[DISPATCH_QOS_NBUCKETS];
    struct dispatch_object_s *dwl_tails[DISPATCH_QOS_NBUCKETS];
    dispatch_workloop_attr_t dwl_attr;
} DISPATCH_ATOMIC64_ALIGN;

#define DISPATCH_LANE_CLASS_HEADER(x) \
    struct dispatch_queue_s _as_dq[0]; \
    DISPATCH_QUEUE_CLASS_HEADER(x, \
    struct dispatch_object_s *volatile dq_items_tail); \
    dispatch_unfair_lock_s dq_sidelock; \
    struct dispatch_object_s *volatile dq_items_head; \
uint32_t dq_side_suspend_cnt

typedef struct dispatch_lane_s {
    DISPATCH_LANE_CLASS_HEADER(lane);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;

// Cache aligned type for static queues (main queue, manager)
struct dispatch_queue_static_s {
    struct dispatch_lane_s _as_dl[0]; \
    DISPATCH_LANE_CLASS_HEADER(lane);
} DISPATCH_CACHELINE_ALIGN;

#define DISPATCH_QUEUE_ROOT_CLASS_HEADER(x) \
    struct dispatch_queue_s _as_dq[0]; \
    DISPATCH_QUEUE_CLASS_HEADER(x, \
    struct dispatch_object_s *volatile dq_items_tail); \
    int volatile dgq_thread_pool_size; \
    struct dispatch_object_s *volatile dq_items_head; \
int volatile dgq_pending

struct dispatch_queue_global_s {
    DISPATCH_QUEUE_ROOT_CLASS_HEADER(lane);
} DISPATCH_CACHELINE_ALIGN;
  • DISPATCH_OBJECT_HEADER中包含dispatch_object_s结构体和_DISPATCH_OBJECT_HEADER

查看_DISPATCH_OBJECT_HEADER的定义:

8E0F9C10-F3EA-4F07-A8CA-DF88BBD7AE10.png

  • 而在 _DISPATCH_OBJECT_HEADER中又包含_os_object_s结构体和OS_OBJECT_STRUCT_HEADER

找到并查看OS_OBJECT_STRUCT_HEADER的定义:

#define OS_OBJECT_STRUCT_HEADER(x) \
    _OS_OBJECT_HEADER(\
    const struct x##_vtable_s *__ptrauth_objc_isa_pointer do_vtable, \
    do_ref_cnt, \
    do_xref_cnt)

再通过 _OS_OBJECT_HEADER 查看其定义:

#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \
        isa; /* must be pointer-sized and use __ptrauth_objc_isa_pointer */ \
        int volatile ref_cnt; \
        int volatile xref_cnt
  • 发现包含isa成员变量。

所以dispatch_queue_s结构体还伪继承于_os_object_s,并包含isa成员变量,说明队列也是一个对象。

同步函数

全局队列

同步函数配合全局队列

dispatch_sync(dispatch_get_global_queue(0, 0), ^{ 
    NSLog(@"Block"); 
});

libdispatch源码中,找到dispatch_sync函数的实现:

27310F86-D7F7-4722-AC10-35EADF4CA95D.png

  • 传入的work参数就是任务的block,只需要一直追寻work参数就能找到调用的代码;

  • 传入_dispatch_sync_f函数的参数3,对work进行包装。

接着看下 参数3 的 实现情况,进入到_dispatch_Block_invoke里面查定义:

4FCFFEDD-F3C4-482B-94AA-DA34BA74C72A.png

  • 从定义里面,我们可以知道,是将block下的invoke强转为dispatch_function_t结构体。

分析完参数,那么接着再看下 _dispatch_sync_f函数的实现:

4C3698B7-CC13-441A-BA81-B68638D4FF62.png

结合上面,再一一对应函数里面的参数:

  • ctxtblock
  • funcinvoke

接着往下找,进入_dispatch_sync_f_inline函数:

DEB4BCB6-C0F6-4BF1-8E4F-8F189E66EC3C.png

从源码里面看,函数中出现了复杂的逻辑,可以使用符号断点,找到正确的逻辑分支。

459CEDB6-36A1-4448-88AD-949AC18F1BC5.png

就可以定位到_dispatch_sync_f_slow函数,进入这个函数里面:

FA8161EF-7927-4EFD-8DB2-0A6AFCD2901B.png

同样是复杂逻辑,继续使用符号断点。

继续在项目中,将所有可能的条件分支,全部设置符号断点: D47E9C26-D250-420D-A5A5-923E834D07D5.png

  • 进入_dispatch_sync_function_invoke函数

进入_dispatch_sync_function_invoke函数:

26180477-05B6-421D-8B70-CE7BCDAE3FB2.png

接着,再进入_dispatch_sync_function_invoke_inline函数:

F646030F-D9CA-4FAD-9F98-4CCB873FC1D7.png

到了这里,就很明白了:

  • _dispatch_thread_frame_push:任务加入队列

  • _dispatch_client_callout:执行任务

  • _dispatch_thread_frame_pop:任务移除队列

进入_dispatch_client_callout函数

84BAAA04-2E45-4CA8-876B-FDBB1B2C6F99.png

  • 调用f(ctxt)就是内部自动调用block任务的代码。

我们再通过lldb进行验证:

753D8F95-65C8-47E8-8AE9-B7484853878A.png

  • 和源码中的逻辑完全一致

同步函数顺序被执行,因为_dispatch_sync_function_invoke_inline中,三个函数的顺序调用:

  • _dispatch_thread_frame_push:任务加入队列

  • _dispatch_client_callout:执行任务

  • _dispatch_thread_frame_pop:任务移除队列

当同步函数加入串行队列,在_dispatch_sync_f_inline函数中,使用_dispatch_barrier_sync_f同步栅栏函数。

并发队列

同步函数配合并发队列:

dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); 
dispatch_sync(queue, ^{ 
    NSLog(@"block---:%@",[NSThread currentThread]); 
});

我们刚刚在前面分析的,dispatch_sync --> _dispatch_sync_f,再根据_dispatch_sync_f的实现:

1E0AB192-AC7B-49FD-BC38-652EEA141EC4.png

再进入 _dispatch_sync_f_inline函数里面查看详情:

4C937C5D-E370-4EDB-BAD2-AB3BFC63392E.png

并发队列触发的代码分支和全局队列有所不同。

进入_dispatch_sync_invoke_and_complete函数:

3A4754CC-446E-4504-946A-65F809FD87EC.png

  • 最终调用的还是_dispatch_sync_function_invoke_inline函数。

函数中的最后一个入参

dispatch_function_t func DISPATCH_TRACE_ARG(void *dc) 

#define DISPATCH_TRACE_ARG(arg) 

#define DISPATCH_TRACE_ARG(arg) , arg
  • ,arg封装到一起,所以外部不用加额外的逗号。

死锁

B3FFCDDE-95E5-446C-BCC3-B53B10B1D6D2.png

  • 当出现死锁异常时,调用_dispatch_sync_f_slow函数
  • 然后调用__DISPATCH_WAIT_FOR_QUEUE__,抛出异常

根据前面几个情况的分析,探索流程:dispatch_sync --> _dispatch_sync_f --> _dispatch_sync_f_inline --> _dispatch_sync_f_inline.

进入_dispatch_sync_f_inline函数:

B7A59BFC-1439-49B0-B540-A5182FE3D224.png

当前为同步函数,并且加入串行队列,调用_dispatch_barrier_sync_f函数,同步函数加入串行队列的底层,使用同步栅栏函数,进入_dispatch_barrier_sync_f函数:

A9174320-9774-4E68-8906-45875E798526.png

接着进入_dispatch_barrier_sync_f_inline函数:

203ED8A5-7C84-45F8-B95D-7932FFF4A910.png

  • 出现死锁异常时,已知的_dispatch_sync_f_slow函数

再进入_dispatch_sync_f_slow函数:

4D543BD3-40B4-4414-B0EF-4A747C18391F.png

  • 内部调用__DISPATCH_WAIT_FOR_QUEUE__抛出异常。

进入__DISPATCH_WAIT_FOR_QUEUE__函数:

115F05E6-6B78-4C7F-977C-C653759638B2.png

  • _dq_state_drain_locked_by函数返回真,抛出异常
  • 传入两个参数,dq_state为队列状态,dsc_waiter为线程的tid

进入_dq_state_drain_locked_by函数:

DFE017CD-9630-41C8-BF52-BC4D75D1788E.png

再进入_dispatch_lock_is_locked_by函数:

5C963D15-AF79-42F4-AD75-4F47A7078AEF.png

  • 找到判断依据 DLOCK_OWNER_MASK

再查看DLOCK_OWNER_MASK定义情况:

C4A1764D-58A3-4713-BD51-A13EF37DC011.png

  • DLOCK_OWNER_MASK定位为很大值,只要一个非零的值和它进行&运算,结果一定不为零;

  • lock_valuetid进行按位异或,它们运算结果为零只有一种可能,就是二者的值相同;

当同步函数加入到串行队列中,同一线程在等待时又被执行,就会形成相互等待的局面,造成死锁的异常。

异步函数

libdispatch源码中,找到dispatch_async函数的实现:

F44A7AFC-4F81-4EF5-9275-2477F1190FE1.png

  • 将任务封装成dispatch_qos_t类型的qos
  • 调用_dispatch_continuation_async函数

任务和优先级的封装

进入_dispatch_continuation_init函数

D8F00AFD-3068-421E-8C71-AA94275129DA.png

进入_dispatch_continuation_init_f函数,对block任务进行封装:

D755BB66-416F-458B-A764-AB8238670EF7.png

进入_dispatch_continuation_priority_set函数,对任务优先级进行封装:

737A7630-A98F-46A4-8554-DACB6875D4F9.png

异步函数的执行,优先级是衡量标准的其中一项。而且任务根据CPU的调度情况异步执行,所以一定是无序的,需要对其进行封装。

并发队列

异步函数配合并发队列

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

dispatch_async(queue, ^{ 
    NSLog(@"block---:%@",[NSThread currentThread]);
});

根据前面的分析: dispatch_async --> _dispatch_continuation_async。再进入 _dispatch_continuation_async 里面,查看实现:

9D9FB3BB-D938-47E4-8FA9-2CF507F2C401.png

接着再查看dx_push的宏定义:

785FDF85-89F5-45E3-815C-5E73F8CDE9F1.png

  • 此时我们关注的是参数3的调用,可以暂时无视dx_vtable,继续跟踪dq_push

  • dq_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_lane_concurrent_push函数:

FA59FF7D-416C-4832-A6C5-A8BD3F8F794F.png

使用符号断点,先进入_dispatch_continuation_redirect_push函数:

30A92E12-A984-4A5D-8F30-FB1A47AB3B5D.png

再次调用dx_push,此时队列类型为queue_pthread_root,所以dx_push不是_dispatch_lane_concurrent_push函数,而是对应了_dispatch_root_queue_push函数,类似于调用了父类的方法:

5652A065-BB00-4408-ADA4-AE46E23F5BDE.png

通过符号断点配合汇编代码查看流程: _dispatch_root_queue_push --> _dispatch_root_queue_push_override --> _dispatch_root_queue_poke --> _dispatch_root_queue_poke_slow.

进入_dispatch_root_queue_poke_slow函数:

static void _dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor) {
    ...
    
    _dispatch_root_queues_init(); 
    
    ...
    
    do { 
        _dispatch_retain(dq);// released in _dispatch_worker_thread 
        while ((r = pthread_create(pthr, attr, _dispatch_worker_thread, dq))) { 
            if (r != EAGAIN) { 
                (void)dispatch_assume_zero(r); 
            } 
        _dispatch_temporary_resource_shortage(); 
    } 
  } 
  while (--remaining);
  
  ...
   
  • 通过_dispatch_root_queues_init注册异步任务执行的回调;
  • 通过do...while循环创建线程,使用pthread_create函数。

当然,我们也可以通过 lldb 调试,打印 堆栈信息,来进行反推:

53EDDB59-FF98-46A3-B2C9-3A20A357A2AF.png

  • 由系统的_pthread_wqthread调用libdispatch_dispatch_worker_thread2函数。

源码中正向推导流程: _dispatch_root_queues_init --> _dispatch_root_queues_init_once --> _dispatch_worker_thread2

_dispatch_worker_thread2进行赋值,封装给pthreadAPI调用:

8E0E8C49-2B7D-46E3-85D2-86C7D310A15C.png

  • 异步线程被CPU调度,系统在合适的时机,通过libsystem_pthread.dylib_pthread_wqthread函数,调用libdispatch.dylib_dispatch_worker_thread2函数,最终执行异步函数的任务

异步任务回调流程: _dispatch_worker_thread2 --> _dispatch_root_queue_drain --> _dispatch_async_redirect_invoke --> _dispatch_continuation_pop --> _dispatch_client_callout --> _dispatch_call_block_and_release 异步函数配合并发队列的逻辑:

  • 首先对任务和优先级进行封装;

  • 多次调用dx_push,最终找到_dispatch_root_queue_push

  • 通过_dispatch_root_queues_init注册异步任务执行的回调;

  • 通过do...while循环创建线程,使用pthread_create函数;

  • 异步线程被CPU调度,系统在合适的时机,通过libsystem_pthread.dylib调用_dispatch_worker_thread2函数,执行回调。

全局队列

异步函数配全局队列

dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 

dispatch_async(queue, ^{
    NSLog(@"block---:%@",[NSThread currentThread]);
});

代码逻辑:dispatch_async --> _dispatch_continuation_async --> _dispatch_continuation_async --> dx_push

此时调用的dx_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_root_queue_push函数流程:

_dispatch_root_queue_push --> _dispatch_root_queue_push_override --> _dispatch_root_queue_push_inline --> _dispatch_root_queue_poke --> _dispatch_root_queue_poke_slow

进入_dispatch_root_queue_poke_slow函数:

  • 通过_dispatch_root_queues_init注册异步任务执行的回调
  • 通过do...while循环创建线程,使用pthread_create函数

使用lldb进行反推:

AF3CE6A9-3F13-4A5A-A861-D269B0E609DB.png

异步任务回调流程: _dispatch_worker_thread2 --> _dispatch_root_queue_drain --> _dispatch_queue_override_invoke --> _dispatch_client_callout --> _dispatch_call_block_and_release

单例模式

static dispatch_once_t onceToken; 

dispatch_once(&onceToken, ^{ 
    NSLog(@"block---:%@",[NSThread currentThread]);
});

来到源码,找到dispatch_once函数的实现:

735B93F7-0AF8-4C5E-A9A4-3F8722F5342D.png

进入dispatch_once_f函数:

3164FA8F-DC9A-41BC-9457-06EA977C95D3.png

  • val强转为dispatch_once_gate_t类型,类似于栅栏的使用 三个条件分支:

  • 如果执行完成,直接返回

  • 如果第一次,执行_dispatch_once_callout函数

  • 如果正在执行,进入_dispatch_once_wait等待

锁的处理

进入_dispatch_once_gate_tryenter函数:

62F90126-DDB1-40FE-8064-328A3C62AA25.png

  • 进行原子锁的处理,防止多线程

执行任务

进入_dispatch_once_callout函数:

7C8984BD-8584-437F-A103-DA207A6B8C3B.png

再进入 _dispatch_client_callout 函数:

B0903FE0-E602-40D2-A61D-15C4CAA74A24.png

  • 通过f(ctxt)执行任务的回调。

接着再看 _dispatch_once_callout 函数里面调用的_dispatch_once_gate_broadcast函数:

FF4960F4-37DB-4AD4-AEE2-67584BF2C8C6.png

  • 锁的处理,并标记为完成

单例模式的原理:

  • 调用dispatch_once函数,传入onceTokenblock。其中onceToken为静态变量,具有唯一性,在底层被强转为dispatch_once_gate_t类型的变量ll通过os_atomic_load函数获取底层原子封装性的关联,得到变量v,通过v来查询任务的状态,如果此时v等于DLOCK_ONCE_DONE,说明任务已经处理过一次了,直接return

  • 如果任务首次执行,将任务进行加锁,任务状态置为DLOCK_ONCE_UNLOCK,目的保证线程安全。加锁后进行block回调函数的执行,执行完成后,将当前任务解锁,将当前的任务状态置为DLOCK_ONCE_DONE,在下次进来时,就不会在执行,会直接返回;

  • 如果在当前任务执行期间,有其他任务进来,会进入无限次等待,原因是当前任务已经获取了锁,进行了加锁,其他任务是无法获取锁的;