OC底层探索22-GCD(上)OC底层探索22-GCD(上)define DISPATCH_QUEUE_WIDTH_FULL 0x1000ulldefine DISPATCH_QUEU

2021-08-09 11:08:46 浏览数 (1)

1、libDispatch源码下载

在项目中增加一个符号断点:dispatch_queue_create;(还可以是其他的GCD-API即可);

  • 可以看到GCD属于libDispatch这个库;

libDispatch源码下载

2、mainQueue 主队列

代码语言:javascript复制
// 重点注释翻译:
// 主队列是用来在应用程序上下文中进行交互的主线程和主runloop。
// 主队列会被自动创建,而且会在main()函数之前创建
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}

#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))
  • 根据变量命名可以看出来dispatch_queue_main_t是一个类型,_dispatch_main_q才是需要操作的对象;就看_dispatch_main_q如何赋值;
代码语言:javascript复制
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,
};
  • 看到了熟悉的com.apple.main-thread",这就是主线程的标示;
  • dq_atomic_flags=1代表串行队列
2.1 mianQueue创建 - libdispatch_init

通过注释翻译我们知道,mian_queue是main函数之前就已经被系统调用了;

代码语言:javascript复制
libdispatch_init(void)
{
    // 方便观察只放出mianQueue有关的源码
#if HAVE_PTHREAD_WORKQUEUE_QOS
    // _dispatch_main_q的部分初始化
    dispatch_qos_t qos = _dispatch_qos_from_qos_class(qos_class_main());
    _dispatch_main_q.dq_priority = _dispatch_priority_make(qos, 0);
#endif

#if DISPATCH_USE_RESOLVERS // rdar://problem/8541707
    _dispatch_main_q.do_targetq = _dispatch_get_default_queue(true);
#endif
    //将当前队列进行绑定
    _dispatch_queue_set_current(&_dispatch_main_q);
    _dispatch_queue_set_bound_thread(&_dispatch_main_q);

}
  • libdispatch_init该方法在dyld中libSystem_initializer()中进行了调用;感兴趣的可以看看OC底层探索16 -应用程序加载的第4部分;
代码语言:javascript复制
static inline void
_dispatch_queue_set_bound_thread(dispatch_queue_class_t dqu)
{
    // Tag thread-bound queues with the owning thread
    // 为当前线程绑定队列
    dispatch_assert(_dispatch_queue_is_thread_bound(dqu));
    uint64_t old_state, new_state;
    os_atomic_rmw_loop2o(dqu._dq, dq_state, old_state, new_state, relaxed, {
        new_state = old_state;
        new_state &= ~DISPATCH_QUEUE_DRAIN_OWNER_MASK;
        new_state |= _dispatch_lock_value_for_self();
    });
}
2.2 小结:
  1. 主线程在初始化方法中进行了初始化和绑定,而且默认为当前线程绑定了队列;
  2. dispatch_get_main_queue方法只是获取,并不是创建;

3、globalQueue 全局队列

代码语言:javascript复制
typedef uint32_t dispatch_qos_t;

dispatch_queue_global_t
dispatch_get_global_queue(intptr_t priority, uintptr_t flags)
{
    // 根据参数priority获取当前线程的优先级    
    dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
    return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}

static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    // 根据优先级从数组中拿出对应的全局并发队列
    return &_dispatch_root_queues[2 * (qos - 1)   overcommit];
}
  • dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),说明这些队列都是并发队列;
  • 系统已经标记了一部分全局的异步队列,方便开发者使用。一般情况下无需在自己创建异步队列

4、createQueue 自定义队列

代码语言:javascript复制
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);
}

static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa, dispatch_queue_t tq, bool legacy) 
{
    // dqa标示串行、并发队列
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

    ...
    // 获取类
    // 并发队列:OS_dispatch_queue_concurrent
    // 串行队列:OS_dispatch_queue_serial
    if (dqai.dqai_concurrent) {
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    
    // dq线程对象进行alloc
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    // dq线程对象进行init
    _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->dq_label = label;
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    // tq=DISPATCH_TARGET_QUEUE_DEFAULT默认队列
    _dispatch_retain(tq);
    dq->do_targetq = tq;

    return _dispatch_trace_queue_create(dq)._dq;
}

*dqai.dqai_concurrent ? DISPATCH_QUEUE_WIDTH_MAX : 1标记了该队列是并发还是串行;

4.1 dq队列进行alloc
代码语言:javascript复制
// 并发队列:OS_dispatch_queue_concurrent
// 串行队列:OS_dispatch_queue_serial

void * _dispatch_object_alloc(const void *vtable, size_t size)
{
    return _os_object_alloc_realized(vtable, size);
}

inline _os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
    _os_object_t obj;
    dispatch_assert(size >= sizeof(struct _os_object_s));
    // 完成队列的内存分配
    while (unlikely(!(obj = calloc(1u, size)))) {
        _dispatch_temporary_resource_shortage();
    }
    // 只能说:卧槽!!!!这不是对象绑定类吗!!!!!
    obj->os_obj_isa = cls;
    return obj;
}
  • 队列是一个对象:_os_object_t;
  • obj->os_obj_isa = cls; 为对象绑定类!
  • 完成了队列类的内存分配以及类型绑定
4.2 dq队列对象进行init

define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull

define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)

理论上最大线程:4096

代码语言:javascript复制
//  width = (dqai.dqai_concurrent ? DISPATCH_QUEUE_WIDTH_MAX : 1)

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;


    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
        }
    }
    // 队列的一些属性设置,在globalQueue中也看到类似的操作
    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;
}
4.3 小结

队列创建底层是_dispatch_lane_create_with_target创建,通过传入的值来确定是串行还是并行队列,dispatch_queue_t也是个对象,也会通过alloc,init进行创建。

5、dispatch_async异步函数

代码语言:javascript复制
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;
    // 将队列dq 和block 任务进行了包装
    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    //这一步涉及到执行
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
        dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
    if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
        _dispatch_trace_item_push(dqu, dc);
    }
#else
    (void)dc_flags;
#endif
    return dx_push(dqu._dq, dc, qos);
}
  • 任务(block)在何时、何处执行完全没有头脑,需要换个方式;
5.1 任务的执行

查看执行堆栈

一步步查看发现在_dispatch_root_queue_drain这一步之后有部分调用堆栈被隐藏了;

  • 异步调用猜测和这个循环是有关系的,也可以叫做轮询;
  • 在自动释放池里进行操作;
  • 一路查找下来才发现block的调用位置,也是在自动释放池里使用函数调用方式;
  • 最后调用_dispatch_call_block_and_release完成了释放;

6、dispatch_sync同步函数

忽略一些无关代码之后的调用栈:

dispatch_sync -> _dispatch_sync_f -> _dispatch_sync_f_inline,重点在_dispatch_sync_f_inline这个函数。

代码语言:javascript复制
static inline void
_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,...)
{
    // 串行队列
    if (likely(dq->dq_width == 1)) {
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }

    // 死锁情况
    if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);
    }

    // 并发队列
    // 对并发队列做一些排序相关的准备任务
    // 排序后,并发队列但是依旧会顺序执行
    _dispatch_introspection_sync_begin(dl);
    // 任务执行
    _dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
            _dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}
  • _dispatch_barrier_sync_f看到了栅栏函数的一些影子,其实同步函数串行队列就是通过栅栏函数来实现了,这部分也会在下一篇中分析;
  • 这个位置出现的死锁情况会在下篇文章中分析;
6.1 串行队列情况

忽略一些无关代码之后的调用栈:

_dispatch_barrier_sync_f -> _dispatch_barrier_sync_f_inline -> _dispatch_lane_barrier_sync_invoke_and_complete

代码语言:javascript复制
static void
_dispatch_lane_barrier_sync_invoke_and_complete(dispatch_lane_t dq,
        void *ctxt, dispatch_function_t func DISPATCH_TRACE_ARG(void *dc))
{
    // 任务(func)的执行
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
    _dispatch_trace_item_complete(dc);
    
    // similar to _dispatch_queue_drain_try_unlock
    // 本次任务执行完成之后进行任务队列的解锁
    os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, release, {
        new_state  = old_state - DISPATCH_QUEUE_SERIAL_DRAIN_OWNED;
        new_state &= ~DISPATCH_QUEUE_DRAIN_UNLOCK_MASK;
        new_state &= ~DISPATCH_QUEUE_MAX_QOS_MASK;
        if (unlikely(old_state & fail_unlock_mask)) {
            os_atomic_rmw_loop_give_up({
                return _dispatch_lane_barrier_complete(dq, 0, flags);
            });
        }
    });
}

// 任务(func)的执行
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);
}
  • os_atomic_rmw_loop2o通过内核发出指令;
  • 同步函数任务是依次执行
6.2 并发队列情况
代码语言:javascript复制
static void
_dispatch_sync_invoke_and_complete(dispatch_lane_t dq, void *ctxt,
        dispatch_function_t func DISPATCH_TRACE_ARG(void *dc))
{
    // 任务执行
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
    _dispatch_trace_item_complete(dc);
    // 任务执行完成之后解锁当前任务队列
    _dispatch_lane_non_barrier_complete(dq, 0);
}

// 本次任务执行完成之后进行队列的解锁
static void
_dispatch_lane_non_barrier_complete(dispatch_lane_t dq, dispatch_wakeup_flags_t flags)
{
    uint64_t old_state, new_state, owner_self = _dispatch_lock_value_for_self();
    // see _dispatch_lane_resume()
    os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, relaxed, {
        new_state = old_state - DISPATCH_QUEUE_WIDTH_INTERVAL;
        if (unlikely(_dq_state_drain_locked(old_state))) {
            new_state |= DISPATCH_QUEUE_DIRTY;
        } else if (likely(_dq_state_is_runnable(new_state))) {
            new_state = _dispatch_lane_non_barrier_complete_try_lock(dq,
                    old_state, new_state, owner_self);
        }
    });
    _dispatch_lane_non_barrier_complete_finish(dq, flags, old_state, new_state);
}
  • 在任务执行之前进行过排序,所以虽然是并发队列但是依旧会顺序执行;
  • _dispatch_sync_function_invoke_inline在分析串行队列的时候就已经分析过这个函数了,就是进行任务的执行和进出栈控制
  • _dispatch_lane_non_barrier_completeos_atomic_rmw_loop2o再次出现,都是内核的相关操作;

总结

在GCD中发现很多操作都会涉及到系统内核,以后有机会还是要去了解一下。

当然即使不太了解内核也不影响我们对GCD底层的基本了解和分析,本文对GCD的串/并队列的创建同步、异步函数执行分析完了。

欢迎在留言和我沟通!

0 人点赞