前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OC底层探索22-GCD(上)OC底层探索22-GCD(上)define DISPATCH_QUEUE_WIDTH_FULL 0x1000ulldefine DISPATCH_QUEU

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

作者头像
用户8893176
发布2021-08-09 11:08:46
5440
发布2021-08-09 11:08:46
举报
文章被收录于专栏:小黑娃Henry

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);

}
代码语言: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的串/并队列的创建同步、异步函数执行分析完了。

欢迎在留言和我沟通!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/6/30 下,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、libDispatch源码下载
  • 2、mainQueue 主队列
  • 3、globalQueue 全局队列
  • 4、createQueue 自定义队列
  • define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
    • 5、dispatch_async异步函数
      • 6、dispatch_sync同步函数
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档