iOS 多线程(二):GCD基础&源码分析上

1,307 阅读9分钟

准备

一、GCD 概念

将任务添加到队列,并且指定执行任务的函数。

什么是GCD?

  • 全称是Grand Central Dispatch
  • C语言,提供了非常多强大的函数。

GCD的优势

  • GCD是苹果公司为多核的并行运算提出的解决方案。
  • GCD会自动利用更多的CPU内核(比如双核、四核)。
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程) 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码。

二、函数和队列

函数

  • 任务使用block封装。
  • 任务的block没有参数也没有返回值。
  • 执行任务的函数
    • 异步 dispatch_async
      • 不用等待当前语句执行完毕,就可以执行下一条语句。
      • 会开启线程执行block的任务。
      • 异步是多线程的代名词。
    • 同步 dispatch_sync
      • 必须等待当前语句执行完毕,才会执行下一条语句。
      • 不会开启线程。
      • 在当前执行block的任务。

队列

image.png

  • 串行队列创建:dispatch_queue_t serial = dispatch_queue_create("ssl", DISPATCH_QUEUE_SERIAL);
  • 并发队列创建:dispatch_queue_t conque = dispatch_queue_create("spring", DISPATCH_QUEUE_CONCURRENT);

看一下dispatch_queue_create的定义:

dispatch_queue_t
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);

函数与队列

  • 同步函数串行队列
    • 不会开启线程,在当前线程执行任务。
    • 任务串行执行,任务一个接着一个。
    • 会产生堵塞。
  • 同步函数并发队列
    • 不会开启线程,在当前线程执行任务。
    • 任务一个接这一个。
  • 异步函数串行队列
    • 开启一条新线程。
    • 任务一个接着一个。
  • 异步函数并发队列
    • 开启线程,在当前线程执行任务。
    • 任务异步执行,没有顺序,CPU调度有关。

主队列与全局队列

  • 主队列
    • 专⻔用来在主线程上调度任务的串行队列dispatch_get_main_queue()
    • 不会开启线程,如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度。
  • 全局并发队列
    • 为了方便程序员的使用,苹果提供了全局队列dispatch_get_global_queue(0, 0)
    • 全局队列是一个并发队列,在使用多线程开发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局队列。

三、面试题

1)

- (void)interview1 {
    NSLog(@"执行1--%@",[NSThread currentThread]);
    
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"执行2--%@",[NSThread currentThread]);
        
        dispatch_sync(queue, ^{
            NSLog(@"执行3--%@",[NSThread currentThread]);
        });
    
        NSLog(@"执行4--%@",[NSThread currentThread]);
    });
    
    NSLog(@"执行5--%@",[NSThread currentThread]);
}

// ***************打印结果(打印1、5、2后卡死)***************    
执行1--<NSThread: 0x6000038460c0>{number = 1, name = main}
执行5--<NSThread: 0x6000038460c0>{number = 1, name = main}
执行2--<NSThread: 0x600003824f40>{number = 6, name = (null)}
  • 首先打印执行1,然后自己创建了一个串行队列,并通过异步函数向这个队列中添加一个任务块(block1)。
  • 异步函数会开启一个子线程并将block1放入子线程中去执行,开启子线程是要耗时的,而且异步任务不需要等待就可以继续执行它后面的代码,所以打印执行5block1前面执行。
  • 再来看block1任务块,先打印执行2,然后通过同步函数添加的block2任务块需要立马执行,而block1所在的队列是串行队列,block1任务块还没执行完,所以要先等block1执行,而block1又要等block2执行完了才能继续往下执行,所以就造成了相互等待而死锁

image.png

2)

- (void)interview2 {

    NSLog(@"执行1--%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"执行2--%@",[NSThread currentThread]);
        
        dispatch_sync(queue2, ^{
            NSLog(@"执行3--%@",[NSThread currentThread]);
        });
        
        NSLog(@"执行4--%@",[NSThread currentThread]);
    });
    
    NSLog(@"执行5--%@",[NSThread currentThread]);
}    

// ***************打印结果*************** 
执行1--<NSThread: 0x600002836180>{number = 1, name = main}
执行5--<NSThread: 0x600002836180>{number = 1, name = main}
执行2--<NSThread: 0x600002830980>{number = 5, name = (null)}
执行3--<NSThread: 0x600002830980>{number = 5, name = (null)}
执行4--<NSThread: 0x600002830980>{number = 5, name = (null)}
  • 这个和面试题和上边相比就是新加了一个队列(不管是串行队列还是并发队列都一样),block1任务块和block2任务块分别放在不同的队列中。
  • 先打印执行1再打印执行5和前面是一样的。然后异步函数会开启子线程去执行block1任务块。
  • block1中先打印执行2,然后通过同步函数向另一个队列中添加block2任务块,由于两个block属于不同的队列,block2可以立马被安排执行而不会死锁,所以接着是打印执行3,最后打印执行4

3)

- (void)interview3 {
    dispatch_queue_t queue = dispatch_queue_create("ssl", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        NSLog(@"1");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    
    dispatch_sync(queue, ^{ NSLog(@"3"); });
    
    NSLog(@"0");
    
    dispatch_async(queue, ^{
        NSLog(@"7");
    });
    dispatch_async(queue, ^{
        NSLog(@"8");
    });
    dispatch_async(queue, ^{
        NSLog(@"9");
    });
}

// ***************打印结果*************** 
12无序 -> 3 -> 0 -> 789无序
  • dispatch_sync(queue, ^{ NSLog(@"3"); });这里是同步函数,所以在这之前要先执行1、2
  • 3执行完直接执行0
  • 7、8、9都是异步函数,又在并发队列中,所以他们是无序的,1、2也是这种情况。

4)

- (void)interview4 {
    while (self.num < 5) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.num++;
        });
    }
    NSLog(@"end : %d",self.num); //
    
    // ***************打印结果*************** 
    >= 5
}
  • 首先一点num的值不能小于5,因为小于5的时候,是出不来while循环的。
  • 上面添加的都是异步函数,这就导致num值为5的时候,可能还有任务在执行self.num++操作,所以num的值有可能大于5

5)

- (void)interview5 {
    for (int i= 0; i<10000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.num++;
        });
    }
    NSLog(@"end : %d",self.num); //
    
    // ***************打印结果*************** 
    < 10000
}
  • 这个面试题和上面的有所不同,这里的条件判断和自增不是用的同一个变量,代码会循环10000次不停的添加self.num++任务,这就导致我们执行++操作时,可能num的值已经变大了,我们还是加的之前的小值,所以最终会< 10000

四、主队列源码分析

打开 libdispatch 源码,进入dispatch_get_main_queue

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

再看一下DISPATCH_GLOBAL_OBJECT的定义:

#define DISPATCH_GLOBAL_OBJECT(type, object) ((type)&(object))
  • 通过定义可以了解到,dispatch_queue_main_ttype_dispatch_main_qobject

既然_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,
};
  • 我们前面有说过主队列是串行队列,这里的DQF_WIDTH(1)就是设置串行的地方,但原理是什么呢,还有dq_serialnum = 1又是什么意思呢,下面进行分析。

五、串行和并发的底层源码辨识

dispatch_queue_create

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

进入_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

进入_dispatch_queue_init

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->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;
    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;
}
  • 调用DQF_WIDTH(width)width是我们上面传入的第三个参数,串行队列时传的1DQF_WIDTH(1),所以也就证明了我们上面说的主队列是串行队列。
  • dq_serialnum的赋值:dq_serialnum = os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed),它的值来自于_dispatch_queue_serial_numbers

找到_dispatch_queue_serial_numbers的赋值:

unsigned long volatile _dispatch_queue_serial_numbers =
DISPATCH_QUEUE_SERIAL_NUMBER_INIT;

再找到DISPATCH_QUEUE_SERIAL_NUMBER_INIT的定义:

// skip zero
// 1 - main_q
// 2 - mgr_q
// 3 - mgr_root_q
// 4,5,6,7,8,9,10,11,12,13,14,15 - global queues
// 17 - workloop_fallback_q
// we use 'xadd' on Intel, so the initial value == next assigned
#define DISPATCH_QUEUE_SERIAL_NUMBER_INIT 17
  • 看到1 - main_q,上面的dq_serialnum = 1也就理解了,是主队列的意思。

_dispatch_queue_attr_to_info

进入_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;
    
    ...
}
  • 串行队列时传入的是NULL,直接返回空dqai

六、GCD底层源码继承链

我们先看一下global_queue的打印情况:

- (void)queueTest {
    dispatch_queue_t globQueue = dispatch_get_global_queue(0, 0);
    NSLog(@"-- %@",globQueue);
}

打印结果*************************
-- <OS_dispatch_queue_global: com.apple.root.default-qos>

搜索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,
	),
	_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,
	),
};
  • 可以看到global队列有多种情况,队列的接收类型是dispatch_queue_t,下面对它的结构进行分析。

点击进入dispatch_queue_t

DISPATCH_DECL(dispatch_queue);

#define DISPATCH_DECL(name) \
        typedef struct name##_s : public dispatch_object_s {} *name##_t
  • ##是连接符,我们还原一下代码:
    typedef struct dispatch_queue_s : public dispatch_object_s {
        
    } *dispatch_queue_t
    
    dispatch_queue_t的本质是dispatch_queue_sdispatch_queue_s继承自dispatch_object_s

查看dispatch_queue_s

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

查看DISPATCH_QUEUE_CLASS_HEADER

#if OS_OBJECT_HAVE_OBJC1
#define _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    DISPATCH_OBJECT_HEADER(x); \
    DISPATCH_UNION_LE(uint64_t volatile dq_state, \
            dispatch_lock dq_state_lock, \
            uint32_t dq_state_bits \
    ); \
    __pointer_sized_field__

查看_DISPATCH_OBJECT_HEADER

#define _DISPATCH_OBJECT_HEADER(x) \
    struct _os_object_s _as_os_obj[0]; \
    OS_OBJECT_STRUCT_HEADER(dispatch_##x); \
    struct dispatch_##x##_s *volatile do_next; \
    struct dispatch_queue_s *do_targetq; \
    void *do_ctxt; \
    union { \
        dispatch_function_t DISPATCH_FUNCTION_POINTER do_finalizer; \
        void *do_introspection_ctxt; \
    }

查看OS_OBJECT_STRUCT_HEADER

#define OS_OBJECT_STRUCT_HEADER(x) \
    _OS_OBJECT_HEADER(\
    const void *_objc_isa, \
    do_ref_cnt, \
    do_xref_cnt); \
    const struct x##_vtable_s *do_vtable
#else

查看_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

七、GCD的任务执行堆栈(同步)

dispatch_sync 探索:

dispatch_sync(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"SSL 函数分析");
});
  • 这是我们平时经常使用的多线程的同步形态,下面进行底层的探索。

查看底层实现:

void
dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
    uintptr_t dc_flags = DC_FLAG_BLOCK;
    if (unlikely(_dispatch_block_has_private_data(work))) {
        return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
    }
    _dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
  • 第二个参数work是我们传进来的block块,_dispatch_Block_invoke(work)这个函数是work的执行,它们作为第二和第三个参数传到了_dispatch_sync_f中,我们去探索一下它们是在哪儿里被执行的。

_dispatch_sync_f:

进入_dispatch_sync_f

static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
        uintptr_t dc_flags)
{
    _dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
}
  • ctxt就是我们传进来的blockfunc是传进来的执行函数,接下来就针对它们的调用进行探索。

进入_dispatch_sync_f_inline

image.png

  • 这里出现了两个return,我们不知道会走哪儿个,接下来添加_dispatch_sync_f_slow_dispatch_sync_recurse两个符号断点,看程序会走哪儿个。

符号断点调试结果:

image.png

  • 通过调试,可以看到block调用时走的是_dispatch_sync_f_slow函数。
_dispatch_sync_f_slow(dispatch_queue_class_t top_dqu, void *ctxt,
		dispatch_function_t func, uintptr_t top_dc_flags,
		dispatch_queue_class_t dqu, uintptr_t dc_flags)
{
    dispatch_queue_t top_dq = top_dqu._dq;
    dispatch_queue_t dq = dqu._dq;
    if (unlikely(!dq->do_targetq)) {
        return _dispatch_sync_function_invoke(dq, ctxt, func);
    }

    pthread_priority_t pp = _dispatch_get_priority();
    struct dispatch_sync_context_s dsc = {
            .dc_flags    = DC_FLAG_SYNC_WAITER | dc_flags,
            .dc_func     = _dispatch_async_and_wait_invoke,
            .dc_ctxt     = &dsc,
            .dc_other    = top_dq,
            .dc_priority = pp | _PTHREAD_PRIORITY_ENFORCE_FLAG,
            .dc_voucher  = _voucher_get(),
            .dsc_func    = func,
            .dsc_ctxt    = ctxt,
            .dsc_waiter  = _dispatch_tid_self(),
    };

    _dispatch_introspection_sync_begin(top_dq);
    _dispatch_trace_item_pop(top_dq, &dsc);
    _dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags
                    DISPATCH_TRACE_ARG(&dsc));
}
  • 调用ctxtfunc的分别是_dispatch_sync_function_invoke_dispatch_sync_invoke_and_complete_recurse,下面继续下符号断点调试。

image.png

  • 可以看到最终是调用了_dispatch_sync_function_invoke函数。

进入_dispatch_sync_function_invoke

static void
_dispatch_sync_function_invoke(dispatch_queue_class_t dq, void *ctxt,
		dispatch_function_t func)
{
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
}

进入_dispatch_sync_function_invoke_inline

static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_class_t dq, void *ctxt,
		dispatch_function_t func)
{
    dispatch_thread_frame_s dtf;
    _dispatch_thread_frame_push(&dtf, dq);
    _dispatch_client_callout(ctxt, func);
    _dispatch_perfmon_workitem_inc();
    _dispatch_thread_frame_pop(&dtf);
}
  • _dispatch_client_callout调用了ctxtfunc

进入_dispatch_client_callout

void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    @try {
        return f(ctxt);
    }
    @catch (...) {
        objc_terminate();
    }
}
  • 可以看到f(ctxt)进行了最终任务的调用。

函数调用流程图:

image.png

堆栈验证:

回到代码,进行堆栈打印:

image.png

  • 在堆栈中我们确实也是找到了_dispatch_client_callout_dispatch_sync_function_invoke调用,完美!!

八、GCD的任务执行堆栈(异步)

dispatch_async:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"SSL 函数分析");
});

源码进入dispatch_async

void
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_init函数对任务优先级进行了封装,为什么异步要进行这些封装呢:
    • 异步调用是无序的,要由优先级来决定谁先调用。
    • 因为是异步的所以要保存起来,在合适的时机进行调用。
  • 任务调用的核心还是在_dispatch_continuation_async函数中。

_dispatch_continuation_init:

进入_dispatch_continuation_init

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;

    dispatch_function_t func = _dispatch_Block_invoke(work);
    
    return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}

进入_dispatch_continuation_init_f

static inline dispatch_qos_t
_dispatch_continuation_init_f(dispatch_continuation_t dc,
		dispatch_queue_class_t dqu, void *ctxt, dispatch_function_t f,
		dispatch_block_flags_t flags, uintptr_t dc_flags)
{
    pthread_priority_t pp = 0;
    
    dc->dc_flags = dc_flags | DC_FLAG_ALLOCATED;
    dc->dc_func = f;
    dc->dc_ctxt = ctxt;
    
    _dispatch_continuation_voucher_set(dc, flags);
    return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
}
  • dc->dc_func = f;dc->dc_ctxt = ctxt;dcfctxt进行了封装,也就是对任务进行了封装。

进入_dispatch_continuation_priority_set

static inline dispatch_qos_t
_dispatch_continuation_priority_set(dispatch_continuation_t dc,
		dispatch_queue_class_t dqu,
		pthread_priority_t pp, dispatch_block_flags_t flags)
{
    dispatch_qos_t qos = DISPATCH_QOS_UNSPECIFIED;
    
    dc->dc_priority = pp;
    
    return qos;
}
  • 这个函数对优先级进行了封装,也是封装到了dc中。

_dispatch_continuation_async:

进入_dispatch_continuation_async

static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
		dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
    ...
    return dx_push(dqu._dq, dc, qos);
}

找到dx_push

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
  • ydcdc中封装了任务和优先级等内容,所以再看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_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,
);

当队列类型是queue_global时,进入_dispatch_root_queue_push

void
_dispatch_root_queue_push(dispatch_queue_global_t rq, dispatch_object_t dou,
		dispatch_qos_t qos)
{
    ...
    _dispatch_root_queue_push_inline(rq, dou, dou, 1);
}

进入_dispatch_root_queue_push_inline

static inline void
_dispatch_root_queue_push_inline(dispatch_queue_global_t dq,
		dispatch_object_t _head, dispatch_object_t _tail, int n)
{
    struct dispatch_object_s *hd = _head._do, *tl = _tail._do;
    if (unlikely(os_mpsc_push_list(os_mpsc(dq, dq_items), hd, tl, do_next))) {
            return _dispatch_root_queue_poke(dq, n, 0);
    }
}

进入_dispatch_root_queue_poke

void
_dispatch_root_queue_poke(dispatch_queue_global_t dq, int n, int floor)
{
    return _dispatch_root_queue_poke_slow(dq, n, floor);
}

进入_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();
    ...
}

进入_dispatch_root_queues_init

static inline void
_dispatch_root_queues_init(void)
{
    dispatch_once_f(&_dispatch_root_queues_pred, NULL,
            _dispatch_root_queues_init_once);
}
  • _dispatch_root_queues_init_once做了线程池的一些相关操作,和相关工作队列的配置。

进入_dispatch_root_queues_init_once

static void
_dispatch_root_queues_init_once(void *context DISPATCH_UNUSED)
{
    _dispatch_fork_becomes_unsafe();
    #if DISPATCH_USE_INTERNAL_WORKQUEUE
    size_t i;
    // 遍历线程池,线程池的一些相关操作
    for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {
            _dispatch_root_queue_init_pthread_pool(&_dispatch_root_queues[i], 0,
                            _dispatch_root_queues[i].dq_priority);
    }
    #else
    int wq_supported = _pthread_workqueue_supported();
    int r = ENOTSUP;

    #if DISPATCH_USE_KEVENT_SETUP
    // 工作队列配置相关
    struct pthread_workqueue_config cfg = {
        .version = PTHREAD_WORKQUEUE_CONFIG_VERSION,
        .flags = 0,
        .workq_cb = 0,
        .kevent_cb = 0,
        .workloop_cb = 0,
        .queue_serialno_offs = dispatch_queue_offsets.dqo_serialnum,
    #if PTHREAD_WORKQUEUE_CONFIG_VERSION >= 2
            .queue_label_offs = dispatch_queue_offsets.dqo_label,
    #endif
    };
    #endif

    cfg.workq_cb = _dispatch_worker_thread2;
    r = pthread_workqueue_setup(&cfg, sizeof(cfg));
    #else
    r = _pthread_workqueue_init(_dispatch_worker_thread2,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
}

进入_dispatch_worker_thread2

image.png

进入_dispatch_root_queue_drain

image.png

进入_dispatch_continuation_pop_inline

image.png

进入_dispatch_continuation_invoke_inline

image.png

进入_dispatch_client_callout

void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    @try {
        return f(ctxt);
    }
    @catch (...) {
        objc_terminate();
    }
}
  • 可以看到f(ctxt)进行了最终任务的调用。

函数调用流程图:

image.png

堆栈验证:

image.png

  • 在堆栈中也是找到了_dispatch_client_callout的调用。