OC底层原理探索—GCD(中)
2021-08-17 本文已影响0人
十年开发初学者
GCD死锁
- (void)deadlock {
// 主线程
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1");
});
}
进入dispatch_sync
->_dispatch_sync_f
->_dispatch_sync_f_inline

通过符号断点会进入
_dispatch_barrier_sync_f
方法,这也和我们的判断一致dq_width == 1
代表串行队列
,进入_dispatch_barrier_sync_f

进入
_dispatch_barrier_sync_f_inline

进入
_dispatch_sync_f_slow
DISPATCH_NOINLINE
static void
_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_queue_t top_dq = top_dqu._dq;
dispatch_queue_t dq = dqu._dq;
if (unlikely(!dq->do_targetq)) {
return _dispatch_sync_function_invoke(dq, ctxt, func);
}
pthread_priority_t pp = _dispatch_get_priority();
struct dispatch_sync_context_s dsc = {
.dc_flags = DC_FLAG_SYNC_WAITER | dc_flags,
.dc_func = _dispatch_async_and_wait_invoke,
.dc_ctxt = &dsc,
.dc_other = top_dq,
.dc_priority = pp | _PTHREAD_PRIORITY_ENFORCE_FLAG,
.dc_voucher = _voucher_get(),
.dsc_func = func,
.dsc_ctxt = ctxt,
.dsc_waiter = _dispatch_tid_self(),
};
//向队列中添加任务
_dispatch_trace_item_push(top_dq, &dsc);
__DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
if (dsc.dsc_func == NULL) {
// dsc_func being cleared means that the block ran on another thread ie.
// case (2) as listed in _dispatch_async_and_wait_f_slow.
dispatch_queue_t stop_dq = dsc.dc_other;
return _dispatch_sync_complete_recurse(top_dq, stop_dq, top_dc_flags);
}
_dispatch_introspection_sync_begin(top_dq);
_dispatch_trace_item_pop(top_dq, &dsc);
_dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags
DISPATCH_TRACE_ARG(&dsc));
}
走到这一不我们不知道哪里引起的死锁,运行程序

发现是在这行代码
__DISPATCH_WAIT_FOR_QUEUE__
引起的崩溃,进入__DISPATCH_WAIT_FOR_QUEUE__

进入
_dq_state_drain_locked_by
->_dispatch_lock_is_locked_by
查看
DLOCK_OWNER_MASK
是个很大的数,也就是lock_value ^ tid = 0
时,才会返回0,也就是说此时使用的队列和当前等待的队列是同一个队列。【结论】死锁是
当前的队列处于等待状态,而又有了新任务过来需要使用这个队列调度,这样就产生了相互等待,进而产生死锁
GCD 单例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
进入dispatch_once

进入
dispatch_once_f
,这里有三个参数onceToken、函数任务、任务的封装

进入
_dispatch_once_gate_tryenter

这里为了保证线程的安全性,通过
_dispatch_lock_value_for_self
上了把锁,保证多线程的安全,如果返回YES,则会去执行_dispatch_once_callout
方法,进入该方法
进入
_dispatch_once_gate_broadcast

将
onceToken
进行原子对比
,如果没有执行过,设置为done
,并对_dispatch_once_gate_tryenter
中的锁进行处理。
回到dispatch_once_f
方法

在代码的最后有个
_dispatch_once_wait
方法
这里主要是为处理在多线程的没有获取到锁的情况下,就会调用
_dispatch_once_wait
方法进行等待,该方法中内部开启了自旋锁
,内部进行原子处理,如果发现其他线程设置了done
,则放弃处理
异步函数

这里是一个单例方法,我们进入
_dispatch_root_queues_init_once
方法
在上篇文章中,我们探索到了异步函数的任务,最终被统一设置为
workq_cb

接着通过
workloop
工作循环调用,也就是说不是立即调用,而是通过os
完成调用,也就说明异步调用的关键是在需要执行的时候能够获取对应的方法,进行异步处理,而同步函数是直接调用。
那么
异步函数的调用到底是在什么时候调用的
,返回上一个方法_dispatch_root_queue_poke_slow
如果是全局队列会进入以下方法

对线程池进行处理,从线程池中获取线程,执行任务,同时判断线程池的变化。

remaining
,可以理解为当前可用线程数,当线程数等于0
时,线程池已满pthread pool is full
,直接return。底层通过pthread
开辟线程
也就是
_dispatch_worker_thread2
是通过pthread
完成oc_atmoic
原子触发。