OC底层探索22-GCD(上)
2021-06-30 本文已影响0人
Henry________
1、libDispatch源码下载
在项目中增加一个符号断点:dispatch_queue_create;(还可以是其他的GCD-API即可);
- 可以看到GCD属于
libDispatch
这个库;
2、mainQueue 主队列
// 重点注释翻译:
// 主队列是用来在应用程序上下文中进行交互的主线程和主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
如何赋值;
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函数之前就已经被系统调用了;
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部分;
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 小结:
-
主线程在初始化方法中进行了初始化和绑定
,而且默认为当前线程绑定了队列; -
dispatch_get_main_queue
方法只是获取,并不是创建;
3、globalQueue 全局队列
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 自定义队列
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
// 并发队列: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
// 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异步函数
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这个函数。
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
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 并发队列情况
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_complete
中os_atomic_rmw_loop2o
再次出现,都是内核的相关操作;
总结
在GCD中发现很多操作都会涉及到系统内核,以后有机会还是要去了解一下。
当然即使不太了解内核也不影响我们对GCD底层的基本了解和分析,本文对GCD的串/并队列的创建
,同步、异步函数执行
分析完了。
欢迎在留言和我沟通!