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

image.png
通过符号断点会进入_dispatch_barrier_sync_f方法,这也和我们的判断一致dq_width == 1代表串行队列,进入_dispatch_barrier_sync_f
image.png
进入_dispatch_barrier_sync_f_inline
image.png
进入_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));
}

走到这一不我们不知道哪里引起的死锁,运行程序

image.png
发现是在这行代码__DISPATCH_WAIT_FOR_QUEUE__引起的崩溃,进入__DISPATCH_WAIT_FOR_QUEUE__
image.png
进入_dq_state_drain_locked_by->_dispatch_lock_is_locked_by查看
image.png
DLOCK_OWNER_MASK是个很大的数,也就是lock_value ^ tid = 0时,才会返回0,也就是说此时使用的队列和当前等待的队列是同一个队列。
【结论】死锁是当前的队列处于等待状态,而又有了新任务过来需要使用这个队列调度,这样就产生了相互等待,进而产生死锁

GCD 单例

   static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
    });

进入dispatch_once

image.png
进入dispatch_once_f,这里有三个参数onceToken、函数任务、任务的封装
image.png
进入_dispatch_once_gate_tryenter
image.png
这里为了保证线程的安全性,通过_dispatch_lock_value_for_self上了把锁,保证多线程的安全,如果返回YES,则会去执行_dispatch_once_callout方法,进入该方法
image.png
进入_dispatch_once_gate_broadcast
image.png
onceToken进行原子对比,如果没有执行过,设置为done,并对_dispatch_once_gate_tryenter中的锁进行处理。

回到dispatch_once_f方法

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

异步函数

image.png
这里是一个单例方法,我们进入_dispatch_root_queues_init_once方法
image.png
在上篇文章中,我们探索到了异步函数的任务,最终被统一设置为workq_cb
image.png
接着通过workloop工作循环调用,也就是说不是立即调用,而是通过os完成调用,也就说明异步调用的关键是在需要执行的时候能够获取对应的方法,进行异步处理,而同步函数是直接调用。
那么异步函数的调用到底是在什么时候调用的,返回上一个方法_dispatch_root_queue_poke_slow
如果是全局队列会进入以下方法
image.png
对线程池进行处理,从线程池中获取线程,执行任务,同时判断线程池的变化。
image.png
remaining,可以理解为当前可用线程数,当线程数等于0时,线程池已满pthread pool is full,直接return。底层通过pthread开辟线程
image.png
也就是_dispatch_worker_thread2是通过pthread完成oc_atmoic原子触发。
上一篇下一篇

猜你喜欢

热点阅读