GCD的处理过程,从全局队列的同步处理任务开始。
先说明一下两个宏定义
#if __GNUC__
#define fastpath(x) ((typeof(x))__builtin_expect((long)(x), ~0l))
#define slowpath(x) ((typeof(x))__builtin_expect((long)(x), 0l))
# define __builtin_expect(expr, val) (expr);
其实就是返回expr;
1. 进入sync中
调用 dispatch_sync_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), reinterpret_cast<void *>(item), _WorkItemRunner);
1.1. 队列回顾
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)获取到的GlobalQueue结构: {
.do_vtable = &_dispatch_queue_root_vtable,
.do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
.do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
.do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK,
.do_ctxt = &_dispatch_root_queue_contexts[2],
.dq_label = "com.apple.root.default-priority",
.dq_running = 2,
.dq_width = UINT32_MAX,//# define UINT32_MAX (4294967295U)
.dq_serialnum = 6,
},
static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = {
.do_type = DISPATCH_QUEUE_GLOBAL_TYPE,
.do_kind = "global-queue",
.do_debug = dispatch_queue_debug,
.do_probe = _dispatch_queue_wakeup_global,
};
_dispatch_queue_wakeup_global 是WakeUp的关键函数,带回儿会详细讲解;
1.2. dispatch_sync_f
同步任务执行的方法接口是dispatch_sync_f
void
dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
typeof(dq->dq_running) prev_cnt;
dispatch_queue_t old_dq;
state1
if (dq->dq_width == 1) {
return dispatch_barrier_sync_f(dq, ctxt, func);
}
state2
// 1) ensure that this thread hasn't enqueued anything ahead of this call
// 2) the queue is not suspended
if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) {
_dispatch_sync_f_slow(dq);
} else {
state3 prev_cnt = dispatch_atomic_add(&dq->dq_running, 2) - 2;
if (slowpath(prev_cnt & 1)) {
if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) {
_dispatch_wakeup(dq);
}
_dispatch_sync_f_slow(dq);
}
}
state4
old_dq = _dispatch_thread_getspecific(dispatch_queue_key);
_dispatch_thread_setspecific(dispatch_queue_key, dq);
func(ctxt);
_dispatch_workitem_inc();
_dispatch_thread_setspecific(dispatch_queue_key, old_dq);
if (slowpath(dispatch_atomic_sub(&dq->dq_running, 2) == 0)) {
_dispatch_wakeup(dq);
}
}
state1
会检查队列允许的并行宽度,若宽度为1,则进入dispatch_barrier_sync_f;
通常private Queue才会设置dq_width为1,保证private queue的FIFO次序。
if (dq->dq_width == 1) { //判定可以并行运行的任务数
return dispatch_barrier_sync_f(dq, ctxt, func);
}
GlobelQueue的Width是MAX值,因此不会进入到上面的代码中;接下来进入到state2;
state2
slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq)
这段代码的或语句的左边判定dq是否存在尾部节点,其实就是判定是否存在任务;
右边的语句,判定dq是不是设置了suspend暂停标记,这个通常用于延时任务;
我们假设第一次插入任务到队列中,那么跳过state2进入state3;
state3
} else {
prev_cnt = dispatch_atomic_add(&dq->dq_running, 2) - 2;
if (slowpath(prev_cnt & 1)) {
if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) {
_dispatch_wakeup(dq);
}
_dispatch_sync_f_slow(dq);
}
}
先通过原子操作将dq_running+2,dq_running表示正在运行的任务数;但为什么要+2啊?
然后判定之前的dq_running的值prev_cnt,若prev_cnt&1!=0,那么进入到上面的if语句中;
这里有个小地方,从目前的代码来看,每次dq_running都是加2,那么若&1不等于0,应该是其他处理过程中设置的;总之在我们这里是不成立的;
而GlobelQueue的prev_cnt为2;因此跳入到state4;
state4
old_dq = _dispatch_thread_getspecific(dispatch_queue_key);
_dispatch_thread_setspecific(dispatch_queue_key, dq);
func(ctxt);
_dispatch_workitem_inc();
_dispatch_thread_setspecific(dispatch_queue_key, old_dq);
if (slowpath(dispatch_atomic_sub(&dq->dq_running, 2) == 0)) {
_dispatch_wakeup(dq);
}
通过dispatch_queue_key获取线程中的私有数据,参看:phtread_key_t介绍
然后将dq保存到线程私有变量上去,说明目前用的queue就是dq;
运行了func(ctext):执行了函数方法,任务在此执行;
然后运行dispatch_atomic_sub(&dq->dq_running, 2),若发现dq_running变成了0,则唤醒dq;这个逻辑需要后面分析才能知道原因了。
总之在全局队列中,这个dq_running在执行同步的时候,是不会存在dq_running-2 =0的情况;
那么同步任务执行就结束了。