iOS

iOS底层探索之多线程(六)—GCD源码分析(sync 同步函数

2021-08-15  本文已影响0人  俊而不逊

回顾

在上篇博客对GCD的不同的队列继续了底层的源码探索分析, 那么本篇博客将继续对GCD的函数继续源码分析。

多线程.png

1. sync 同步函数

我们都知道 GCD底层是用C写的,封装了 block函数来执行添加的任务,那么这个 block底层是如何封装的呢?

dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"GCD函数分析");
   });

在源码里面搜索dispatch_sync

dispatch_sync

我们看的是block也就是第二个参数work,直接看 work去哪里了就行,直接定位在最后一行代码

_dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);

搜索_dispatch_Block_invoke

_dispatch_Block_invoke

那么现在去看看这 block的包装函数_dispatch_sync_f在哪里调用了,通过搜索找到了一个中间层包装,如下代码:

static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
        uintptr_t dc_flags)
{
    _dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
}

再去搜索_dispatch_sync_f_inline

_dispatch_sync_f_inline
_dispatch_sync_f_inline里面if判断太多了,不知道该看哪个了,改怎么办呢?这个源码是不能编译运行的!
改怎么办呢???
靓仔,不要慌!我们可以在调用函数的地方,下不同的符号断点,看看走哪个😊

然后继续在源码里面搜索_dispatch_sync_f_slow

这一波操作,就很细节,666,还有谁能把GCD源码探索的这么清新脱俗,45 度仰望天空,我这该死的无处安放的魅力!

666

2. asycn 异步函数

异步也是一样, 那么我们来搜索一波

在前面的那个,是直接返回了,但是这里做了一层包装

dc->dc_func = f;
dc->dc_ctxt = ctxt;

包装完了,还有一个对优先级的处理

return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
_dispatch_continuation_priority_set
我们再回到dispatch_async函数里面
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;

    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    _dispatch_continuation_async(dq, dc, qos, dc->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);
}

这里就会迷失方向了,这个dx_push是个什么东西呢?搜索了下,找到了下面这个宏定义

dx_push
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

在宏定义里面dx_vtable不是我们需要看的,我们去找dq_push,这个第三个参数 z 就是 qos,看看dq_push在哪里使用了

dq_push调用

底层为不同类型的队列提供不同的调用入口,比如全局并发队列会调用_dispatch_root_queue_push方法。依此作为入口,全局搜索_dispatch_root_queue_push方法的实现:

_dispatch_root_queue_push
前面的代码只是做一些判断封装处理,最终会走到最后一行代码_dispatch_root_queue_push_inline中,继续跟踪器源码流程:
_dispatch_root_queues_init(void)
{
    dispatch_once_f(&_dispatch_root_queues_pred, NULL,
            _dispatch_root_queues_init_once);
}

这里是一个dispatch_once_f单例,有个参数_dispatch_root_queues_init_once,继续搜索

同时这里有一个关键的设置,执行函数的设置,也就是将任务执行的函数被统一设置成了_dispatch_worker_thread2 ,如下代码:

cfg.workq_cb = _dispatch_worker_thread2;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));

通过bt打印程序的运行堆栈信息,来验证异步函数最终任务是通过_dispatch_worker_thread2调用的

_dispatch_worker_thread2
控制台打印的堆栈信息,和我们探索推理的结果是,一模模一样样😁,就问靓仔你服不服!哈哈😁!
还有谁

3. 总结

更多内容持续更新

🌹 喜欢就点个赞吧👍🌹

🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹

🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹

上一篇下一篇

猜你喜欢

热点阅读