上一篇我们了解了dispatch_queue_t的数据结构和main queue、global queue、user queue之间的参数差别,这一章我们来分析下GCD的方法
这里的fastpath(x)和slowpath(x)就相当于x,只是加了cpu指令优化,所以if(slowpath(x))相当于if(x)
dispatch_async()
https://opensource.apple.com/tarballs/libdispatch/
void
dispatch_async(dispatch_queue_t dq, void (^work)(void))
{
dispatch_async_f(dq, _dispatch_Block_copy(work),
_dispatch_call_block_and_release);
}
//调用了下面的方法
void
dispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
//1、定义一个封装的block操作
dispatch_continuation_t dc;
// No fastpath/slowpath hint because we simply don't know
//2、dq_width == 1表示串行,就是main queue和用户创建的串行queue
if (dq->dq_width == 1) {
return dispatch_barrier_async_f(dq, ctxt, func);
}
//如果是global queue和用户创建的并行queue则继续向下走
//3、线程中有个dispatch_continuation_t缓存链表,如果获取到就把链表的下一个设为缓存,相当于把第一个取出来了
dc = fastpath(_dispatch_continuation_alloc_cacheonly());
if (!dc) {
//4、如果没有dispatch_continuation_t缓存在堆上创建一个,并且初始化后调用_dispatch_queue_push
return _dispatch_async_f_slow(dq, ctxt, func);
}
//5、初始化dispatch_continuation_t,把block封装成dispatch_continuation_t
dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT;
dc->dc_func = func;
dc->dc_ctxt = ctxt;
// No fastpath/slowpath hint because we simply don't know
//6、如果do_targetq存在,则任务有do_targetq来执行
if (dq->do_targetq) {
return _dispatch_async_f2(dq, dc);
}
//7、把dispatch_continuation_t放到queue的执行列表中
_dispatch_queue_push(dq, dc);
}
//dispatch_continuation_t的结构,封装的一个block操作
struct dispatch_continuation_s {
const void *do_vtable;
struct dispatch_continuation_s *volatile do_next;
dispatch_function_t dc_func;
void *dc_ctxt
dispatch_group_t dc_group;
void *dc_data[3];
};
通过上面的方法我们可以看出,dispatch_async_f最终将block封装成dispatch_continuation_s并调用_dispatch_queue_push放到对应的queue的执行链表结尾,global queue是放到自己的执行链表执行,main queue和user queue放到do_targetq的执行链表执行
下面我们再来分析_dispatch_queue_push()方法
#define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y))
#define _dispatch_queue_push_list _dispatch_trace_queue_push_list
//最终是调用了下面的方法
static inline void
_dispatch_trace_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head,
dispatch_object_t _tail)
{
if (slowpath(DISPATCH_QUEUE_PUSH_ENABLED())) {
struct dispatch_object_s *dou = _head._do;
do {
//如果_head和_tail不相同时进行处理
_dispatch_trace_continuation(dq, dou, DISPATCH_QUEUE_PUSH);
} while (dou != _tail._do && (dou = dou->do_next));
}
//最后调用这个方法把封装的操作放到queue的执行链表
_dispatch_queue_push_list(dq, _head, _tail);
}
//真正放入链表的方法
static inline void
_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head,
dispatch_object_t _tail)
{
struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do;
tail->do_next = NULL;
dispatch_atomic_store_barrier();
//把dq->dq_items_tail与tail交换并把之前的值返回
prev = fastpath(dispatch_atomic_xchg2o(dq, dq_items_tail, tail));
//如果queue中存在还未执行的链表,则把push的链表头接到原来的尾部
if (prev) {
// if we crash here with a value less than 0x1000, then we are at a
// known bug in client code for example, see _dispatch_queue_dispose
// or _dispatch_atfork_child
prev->do_next = head;
} else {
_dispatch_queue_push_list_slow(dq, head);
}
}
//如果queue中没有未完成的任务,则push进来的链表头直接设置成queue的待执行链表头,并且唤醒queue的线程
void
_dispatch_queue_push_list_slow(dispatch_queue_t dq,
struct dispatch_object_s *obj)
{
// The queue must be retained before dq_items_head is written in order
// to ensure that the reference is still valid when _dispatch_wakeup is
// called. Otherwise, if preempted between the assignment to
// dq_items_head and _dispatch_wakeup, the blocks submitted to the
// queue may release the last reference to the queue when invoked by
// _dispatch_queue_drain. <rdar://problem/6932776>
_dispatch_retain(dq);
dq->dq_items_head = obj;
_dispatch_wakeup(dq);
_dispatch_release(dq);
}
dispatch_async()总结:
1、先将block封装成dispatch_continuation_s结构体
2、如果目标queue的do_targetq不存在,则把封装的dispatch_continuation_s插入到目标待执行链表尾部
3、如果目标queue的do_targetq存在,则把封装的dispatch_continuation_s出入到do_targetq的待执行链表尾部
4、如果queue的待执行链表存在,一般情况下说明queue正在运行,直接插入新的dispatch_continuation_s就可以
5、如果queue的待执行链表不存在,说明queue是没有任务暂停状态的,把dispatch_continuation_s插入链表后唤醒queue的线程