美文网首页
iOS看源码:GCD(一)

iOS看源码:GCD(一)

作者: FireStroy | 来源:发表于2020-07-11 23:31 被阅读0次

GCD队列的创建

    dispatch_queue_t queue1 = dispatch_queue_create("ZFD1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("ZFD2", NULL);

通过符号断点"dispatch_queue_create"可看到函数实在libdispatch.dylib中 GCD已经开源 libdispatch源码

来到GCD源码 队列创建指向了

_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{…}

创建队列时 指定队列类型 是通过dispatch_queue_attr_t dqa这个参数来区分的。
函数内部唯一使用dqa参数的地方就是第一行的

dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

明显 _dispatch_queue_attr_to_info()就是队列信息的来源
下面看看dispatch_queue_attr_info_t dqai是什么

typedef struct dispatch_queue_attr_info_s {
    dispatch_qos_t dqai_qos : 8;
    int      dqai_relpri : 8;
    uint16_t dqai_overcommit:2;
    uint16_t dqai_autorelease_frequency:2;
    uint16_t dqai_concurrent:1;
    uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;

感觉很熟悉 是不是很像isa_t的结构体位域的定义
那么_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 = { };
        //串行队列直接return
    if (!dqa) return dqai;
        //并发队列
        ...初始赋值操作
    return dqai;
}

可以看到 默认的串行队列返回的dqai里面什么也没有 而并发队列则有一系列的位域的操作。

回到_dispatch_lane_create_with_target()这里 继续看队列的创建

DISPATCH_NOINLINE
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);
  ...
   
}

其中 tq 是默认带来的参数 DISPATCH_TARGET_QUEUE_DEFAULT 定义就是 NULL

这个函数最后返回的是

return _dispatch_trace_queue_create(dq)._dq;

返回结果肯定是一个dispatch_queue_t类型
往前推 那么队列的创建应该就是和dq有关

_dispatch_trace_queue_create(dq)返回的是一个dispatch_queue_class_t类型

typedef union {
    struct dispatch_queue_s *_dq;
    struct dispatch_workloop_s *_dwl;
    struct dispatch_lane_s *_dl;
    struct dispatch_queue_static_s *_dsq;
    struct dispatch_queue_global_s *_dgq;
    struct dispatch_queue_pthread_root_s *_dpq;
    struct dispatch_source_s *_ds;
    struct dispatch_mach_s *_dm;
    dispatch_lane_class_t _dlu;
#ifdef __OBJC__
    id<OS_dispatch_queue> _objc_dq;
#endif
} dispatch_queue_class_t
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->do_targetq = tq;
return _dispatch_trace_queue_create(dq)._dq;
typedef struct dispatch_lane_s {
    DISPATCH_LANE_CLASS_HEADER(lane);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
struct dispatch_queue_s {
    DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;

dq.do_targetq = tq;
tq的获取:

tq = _dispatch_get_root_queue(
                qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, 
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
        DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
    }
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

struct dispatch_queue_global_s _dispatch_root_queues[]
tq队列是从全局静态数组中通过qos和overcommit来获取的。

_dispatch_root_queues的初始化实在dispatch_init()时初始化创建的
libdispatcdispatch_queue_createh_init()->_dispatch_introspection_init->_dispatch_trace_queue_create(&_dispatch_root_queues[i]);
_dispatch_trace_queue_create(&_dispatch_main_q); _dispatch_trace_queue_create(&_dispatch_mgr_q);
主队列和全局队列也是在这个时候创建的

死锁

GCD的同步函数

dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
    uintptr_t dc_flags = DC_FLAG_BLOCK;
    ...
    _dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}

_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
     if (likely(dq->dq_width == 1)) {//串行队列
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }
    …
}

最终来到

_dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    dispatch_tid tid = _dispatch_tid_self();
    if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
        DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
    }
    dispatch_lane_t dl = upcast(dq)._dl;
    // 死锁
    if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dl, tid))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, DC_FLAG_BARRIER, dl,
                DC_FLAG_BARRIER | dc_flags);
    }

    if (unlikely(dl->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dl, ctxt, func,
                DC_FLAG_BARRIER | dc_flags);
    }
    _dispatch_introspection_sync_begin(dl);
    _dispatch_lane_barrier_sync_invoke_and_complete(dl, ctxt, func
            DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
                    dq, ctxt, func, dc_flags | DC_FLAG_BARRIER)));
}

注意 _dispatch_sync_f_slow() 这个函数是不是有点眼熟 GCD死锁的时候 调用堆栈里就能看到这个函数。

_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_trace_item_push(top_dq, &dsc);
    __DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
…
}
__DISPATCH_WAIT_FOR_QUEUE__(dispatch_sync_context_t dsc, dispatch_queue_t dq)
{
    uint64_t dq_state = _dispatch_wait_prepare(dq);
    if (unlikely(_dq_state_drain_locked_by(dq_state, dsc->dsc_waiter))) {
        DISPATCH_CLIENT_CRASH((uintptr_t)dq_state,
                "dispatch_sync called on queue "
                "already owned by current thread");
    }
…
}

dsc->dsc_waiter是当前线程的tid dq_state是当前队列的状态
这个状态标记录了当前queue的tid
通过_dq_state_drain_locked_by查询当前队列lock的tid和传进来的tid是否一致 如果一致说明这个队列已经被当前线程lock 造成死锁。
大致理解为:
线程A 在串行队列Q 中加入task1那么Q.dq_state会和A.tid第一次绑定并阻塞线程等待task1执行
如果此时task1向队列Q又同步提交task2 ,那么Q.dq_state会查询到Q队列已经和线程A绑定过 还没有解绑 那么会触发死锁异常。

相关文章

  • iOS看源码:GCD(一)

    GCD队列的创建 通过符号断点"dispatch_queue_create"可看到函数实在libdispatch....

  • GCD源码分析(上)

    GCD源码官网下载地址 GCD的源码在我看来,一直是iOS源码中最晦涩难懂一种,因为涉及到太多的宏定义和结构定义,...

  • 多线程

    iOS常见的多线程方案 GCD源码:https://github.com/apple/swift-corelibs...

  • OC-多线程GCD

    参考:GCD源码深入理解 GCDiOS多线程--彻底学会多线程之『GCD』关于iOS多线程,我说,你听,没准你就懂...

  • iOS源码

    1.GCD源码2.iOS设计模式3.GNUstep:Cocoa的OC库重新开源实现4.obj4源码5.CFRunL...

  • iOS开发多线程之GCD

    iOS开发多线程之GCDiOS开发之GCD同步任务加强iOS开发之GCD串行队列iOS开发之GCD并发队列 GCD...

  • iOS isKindOfClass isMemberOfClas

    iOS isKindOfClass isMemberOfClass 底层源码原理解析及练习 直接看源码 看源码大家...

  • 源码剖析:探究 Repeat 中 GCD 的应用

    源码剖析:探究 Repeat 中 GCD 的应用源码剖析:探究 Repeat 中 GCD 的应用

  • [iOS 多线程] iOS多线程-GCD

    iOS多线程-GCD GCD的简介 GCD,全称为 Grand Central Dispatch ,是iOS用来管...

  • iOS多线程:『GCD』详尽总结

    iOS多线程:『GCD』详尽总结 iOS多线程:『GCD』详尽总结

网友评论

      本文标题:iOS看源码:GCD(一)

      本文链接:https://www.haomeiwen.com/subject/lfoicktx.html