iOS-GCD源码解析(二)dispatch_async的实现

上一篇我们了解了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的线程

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。