准备
一、GCD 概念
将任务添加到队列,并且指定执行任务的函数。
什么是GCD?
- 全称是
Grand Central Dispatch
。 - 纯
C
语言,提供了非常多强大的函数。
GCD的优势
GCD
是苹果公司为多核的并行运算提出的解决方案。GCD
会自动利用更多的CPU
内核(比如双核、四核)。GCD
会自动管理线程的生命周期(创建线程、调度任务、销毁线程) 程序员只需要告诉GCD
想要执行什么任务,不需要编写任何线程管理代码。
二、函数和队列
函数
- 任务使用
block
封装。 - 任务的
block
没有参数也没有返回值。 - 执行任务的函数
- 异步
dispatch_async
- 不用等待当前语句执行完毕,就可以执行下一条语句。
- 会开启线程执行
block
的任务。 - 异步是多线程的代名词。
- 同步
dispatch_sync
- 必须等待当前语句执行完毕,才会执行下一条语句。
- 不会开启线程。
- 在当前执行
block
的任务。
- 异步
队列
- 串行队列创建:
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
放入子线程中去执行,开启子线程是要耗时的,而且异步任务不需要等待就可以继续执行它后面的代码,所以打印执行5
在block1
前面执行。 - 再来看
block1
任务块,先打印执行2
,然后通过同步函数添加的block2
任务块需要立马执行,而block1
所在的队列是串行队列,block1
任务块还没执行完,所以要先等block1
执行,而block1
又要等block2
执行完了才能继续往下执行,所以就造成了相互等待而死锁
。
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");
});
}
// ***************打印结果***************
1、2无序 -> 3 -> 0 -> 7、8、9无序
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_t
是type
,_dispatch_main_q
是object
。
既然_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_info
对dqa
进行面向对象的包装,为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
是我们上面传入的第三个参数,串行队列时传的1
即DQF_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_s
,dispatch_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
就是我们传进来的block
,func
是传进来的执行函数,接下来就针对它们的调用进行探索。
进入_dispatch_sync_f_inline
:
- 这里出现了两个
return
,我们不知道会走哪儿个,接下来添加_dispatch_sync_f_slow
和_dispatch_sync_recurse
两个符号断点
,看程序会走哪儿个。
符号断点
调试结果:
- 通过调试,可以看到
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));
}
- 调用
ctxt
和func
的分别是_dispatch_sync_function_invoke
和_dispatch_sync_invoke_and_complete_recurse
,下面继续下符号断点调试。
- 可以看到最终是调用了
_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
调用了ctxt
和func
。
进入_dispatch_client_callout
:
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
- 可以看到
f(ctxt)
进行了最终任务的调用。
函数调用流程图:
堆栈验证:
回到代码,进行堆栈打印:
- 在堆栈中我们确实也是找到了
_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;
,dc
对f
和ctxt
进行了封装,也就是对任务进行了封装。
进入_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)
y
是dc
,dc
中封装了任务和优先级等内容,所以再看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
:
进入_dispatch_root_queue_drain
:
进入_dispatch_continuation_pop_inline
:
进入_dispatch_continuation_invoke_inline
:
进入_dispatch_client_callout
:
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
- 可以看到
f(ctxt)
进行了最终任务的调用。
函数调用流程图:
堆栈验证:
- 在堆栈中也是找到了
_dispatch_client_callout
的调用。