iOS学习

iOS-底层原理25-GCD(下)

2021-01-28  本文已影响0人  一亩三分甜

《iOS底层原理文章汇总》
上一篇文章《iOS-底层原理24-GCD(上)》介绍了异步函数disasync的包装和调用流程,本文介绍线程是怎么被GCD封装创建的

1.队列的创建以模板进行处理:基础模板的基础上进行修改

DISPATCH_ALWAYS_INLINE
static inline dispatch_introspection_queue_s
_dispatch_introspection_lane_get_info(dispatch_lane_class_t dqu)
{
    dispatch_lane_t dq = dqu._dl;
    bool global = _dispatch_object_is_global(dq);
    uint64_t dq_state = os_atomic_load2o(dq, dq_state, relaxed);

    dispatch_introspection_queue_s diq = {
        .queue = dq->_as_dq,
        .target_queue = dq->do_targetq,
        .label = dq->dq_label,
        .serialnum = dq->dq_serialnum,
        .width = dq->dq_width,
        .suspend_count = _dq_state_suspend_cnt(dq_state) + dq->dq_side_suspend_cnt,
        .enqueued = _dq_state_is_enqueued(dq_state) && !global,
        .barrier = _dq_state_is_in_barrier(dq_state) && !global,
        .draining = (dq->dq_items_head == (void*)~0ul) ||
                (!dq->dq_items_head && dq->dq_items_tail),
        .global = global,
        .main = dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE,
    };
    return diq;
}
image.png

怎么知道如下进入_dispatch_continuation_redirect_push还是_dispatch_lane_push,通过下符号断点的方式知道进入_dispatch_continuation_redirect_push之后进入_dispatch_root_queue_push


image.png
image.png
image.png
image.png

2.pthread创建线程过程

以上程序进入_dispatch_root_queue_push后进入_dispatch_root_queue_push_inline(rq, dou, dou, 1)符号断点验证进入_dispatch_root_queue_poke_slow


image.png
image.png

3.GCD单例:流程如何控制一次?怎么保证线程安全?

单例dispatch_once.png

线程安全,第一次还没访问到这把锁,第二次又进入会进入等待状态,始终返回不了,可以对延迟做设置_dispatch_once_wait(l)

4.上节课的堆栈信息中pthread创建线程后libsystem_pthread.dylib_pthread_wqthread,怎么调用到libdispatch.dylib_dispatch_worker_thread2的呢?

在_dispatch_root_queues_init_once方法中注册_dispatch_worker_thread2,经过系统级别的os_atomic_load2o(dq, dgq_thread_pool_size, ordered)进行调用,之后还有回调completeHandler

* thread #4, queue = 'cooci', stop reason = breakpoint 1.1
  * frame #0: 0x000000010b60b157 001---函数与队列`__29-[ViewController viewDidLoad]_block_invoke(.block_descriptor=0x000000010b60e0e8) at ViewController.m:42:9
    frame #1: 0x000000010b876f11 libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #2: 0x000000010b877e8e libdispatch.dylib`_dispatch_client_callout + 8
    frame #3: 0x000000010b87a7a3 libdispatch.dylib`_dispatch_continuation_pop + 552
    frame #4: 0x000000010b879bbb libdispatch.dylib`_dispatch_async_redirect_invoke + 771
    frame #5: 0x000000010b889399 libdispatch.dylib`_dispatch_root_queue_drain + 351
    frame #6: 0x000000010b889ca6 libdispatch.dylib`_dispatch_worker_thread2 + 135
    frame #7: 0x00007fff523019f7 libsystem_pthread.dylib`_pthread_wqthread + 220
    frame #8: 0x00007fff52300b77 libsystem_pthread.dylib`start_wqthread + 15
image.png
image.png
image.png

5.栅栏函数dispatch_barrier_async(异步栅栏函数不会堵塞当前所在线程后面语句)和dispatch_barrier_sync(同步栅栏函数会堵塞当前所在线程后面语句),堵塞传入的队列(只能是自定义并发队列),直到前面的任务执行完,后面的栅栏函数中的block才会执行

6.同步函数,栅栏函数,死锁底层源码分析

1.同步函数dispatch_sync,栅栏函数dispatch_barrier_async底层原理
2.模拟死锁:同步函数堵塞主线程


image.png

死锁堆栈中信息_dispatch_sync_f_slow


image.png
当前任务要执行又要等待,处于矛盾中,会死锁
image.png
image.png

死锁只需判断当前要执行的任务和要等待的任务是不是同一个任务,而不管是在主线程还是子线程中都有可能发生死锁

7.信号量分析

很多时候GCD控制并发数比较难,可以通过设置信号量控制最大并发数,信号量设置为2,任务两两执行


image.png

若信号量设置为1,则任务一个一个执行,起到同步的效果


image.png
image.png
image.png
上一篇 下一篇

猜你喜欢

热点阅读