美文网首页
iOS 底层原理 - GCD

iOS 底层原理 - GCD

作者: yan0_0 | 来源:发表于2020-09-23 10:39 被阅读0次

    GCD简介

    什么是GCD?
    全称是 Grand Central Dispatch
    纯 C 语言,提供了非常多强大的函数 GCD的优势
    GCD 是苹果公司为多核的并行运算提出的解决方案
    GCD 会自动利用更多的CPU内核(比如双核、四核)
    GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程) 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码

    函数

    将任务添加到队列,并且指定执行任务的函数
    任务使用 block 封装

    • 任务的 block 没有参数也没有返回值
    • 执行任务的函数
    • 异步 dispatch_async
    • 不用等待当前语句执行完毕,就可以执行下一条语句 * 会开启线程执行 block 的任务
    • 异步是多线程的代名词
    • 同步 dispatch_sync
    • 必须等待当前语句执行完毕,才会执行下一条语
    • 不会开启线程
    • 在当前执行 block 的任务

    队列

    屏幕快照 2020-08-19 下午11.11.05.png

    队列的创建

    #define DISPATCH_TARGET_QUEUE_DEFAULT NULL
    
    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);
    }
    
    

    其参数如下:

    const char *label: 队列的唯一标识符,可以传空值。
    dispatch_queue_attr_t attr: 标识队列的类型,区分是串行队列还是并发队列。

    DISPATCH_QUEUE_SERIAL: 串行队列
    DISPATCH_QUEUE_CONCURRENT: 并发队列

    串行队列的创建方法

    dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
    

    常用的主队列就是串行队列,dispatch_get_main_queue()

    专门用在主线程调度任务的队列,也称UI队列
    不会再开启线程
    如果有任务执行,再添加其他任务,会被堵塞

    并发队列的创建方法

    dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    

    常用的全局队列就是并发队列dispatch_get_global_queue(long identifier, unsigned long flags),它可以直接执行异步任务。该方法第一个参数是优先级,全局队列的优先级为DISPATCH_QUEUE_PRIORITY_DEFAULT,这个值是一个为0的宏,所以也可以传0。unsigned long flags: 苹果官方文档的解释是Flags that are reserved for future use。标记这个参数是为了未来使用保留的,现在传0即可。
    此处引入线程的优先级概念,优先级越高越先执行。

    DISPATCH_QUEUE_PRIORITY_HIGH: 2
    DISPATCH_QUEUE_PRIORITY_DEFAULT: 0
    DISPATCH_QUEUE_PRIORITY_LOW: (-2)
    DISPATCH_QUEUE_PRIORITY_BACKGROUND: INT16_MIN

    队列和函数组合

    屏幕快照 2020-08-19 下午11.15.37.png

    看下例子一

    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            NSLog(@"4");
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
        });
        NSLog(@"5");
    

    打印结果为1,5,2,4,3,先是顺序执行,异步队列不会阻塞,但是会耗时,所以会慢些

    例子二,选择题

    dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
    
        /***
         1 2 3
         0
         7 8 9
         */
        
        dispatch_async(queue, ^{
            // sleep(2);
            NSLog(@"1");
        });
        dispatch_async(queue, ^{
            NSLog(@"2");
        });
        // 堵塞 - 护犊子
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        // **********************
        NSLog(@"0");
    
        dispatch_async(queue, ^{
            NSLog(@"7");
        });
        dispatch_async(queue, ^{
            NSLog(@"8");
        });
        dispatch_async(queue, ^{
            NSLog(@"9");
        });
    
        // A: 1230789
        // B: 1237890
        // C: 3120798
        // D: 2137890
    

    答案AC,1,2开启异步线程,代码在137行堵塞,阻塞耗时比异步队列耗时久,所以0在1,2,3之后运行,0在主线程,7,8,9在它之后开启异步线程,耗时,所以0在7,8,9之前

    例子三

     // 同步队列
        dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
        NSLog(@"1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    

    打印结果,1,5,2,产生死锁崩溃,首先是一个串行队列,1,5线执行,然后执行异步函数里的任务,2,同步代码块,4,在4这里产生阻塞,需要等同步代码块执行完,同步代码块里有任务3,但是串行队列的原则是先进先出,所以3需要等4任务执行完后才能执行,产生死锁。


    屏幕快照 2020-08-20 上午12.10.28.png

    如果把4任务提前会死锁吗

    // 同步队列
        dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
        NSLog(@"1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            NSLog(@"4");
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            
        });
        NSLog(@"5");
    

    打印结果 1,5,2,4,崩溃,因为任务3和异步函数形成相互等待,使用方面要特别注意串行队列和同步函数。判断是否发生死锁的最好方法就是看有没有在串行队列(当然也包括主队列)中向这个队列添加任务。

    死锁

    • 主线程因为你同步函数的原因等着先执行任务
    • 主队列等着主线程的任务执行完毕再执行自己的任务
    • 主队列和主线程相互等待会造成死锁

    队列是如何创建的

    我们通过dispatch_queue_create创建队列,我们点进去发现看不到其他的,需要配合源码分析,首先我们在dispatch_queue_create处下一个dispatch_queue_create的符号断点,可以看到它在libdispatch动态库里面,在Apple open source里下载对应的源码。

    屏幕快照 2020-08-21 上午7.56.43.png

    打开源码,搜索dispatch_queue_create( 发现搜出来很多,因为我们穿的第一个参数是字符串,搜索dispatch_queue_create(con,结果如下


    屏幕快照 2020-08-21 上午8.03.09.png

    来到这里

    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);
    }
    

    再往下进入方法_dispatch_lane_create_with_target

    屏幕快照 2020-08-23 下午10.04.11.png

    QOS:类似于优先级,在创建global queue的时候的第一个参数就是priority或者QOS。

    over commit:如果当前队列没有可以分配的空余线程,就开启一个新线程来做任务

    我们创建队列主要是通过第二个参数区分是串行还是并行,也就是重点在于dispatch_queue_attr_t dqa这里。
    搜索_dispatch_queue_attr_to_info方法,来到这里

    ispatch_queue_attr_info_t
    _dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
    {
        dispatch_queue_attr_info_t dqai = { };
            //默认是串行队列,如果传NULL返回
        if (!dqa) return dqai;
    
    #if DISPATCH_VARIANT_STATIC
        if (dqa == &_dispatch_queue_attr_concurrent) {
            dqai.dqai_concurrent = true;
            return dqai;
        }
    #endif
    
        if (dqa < _dispatch_queue_attrs ||
                dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
            DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
        }
    
        // 苹果的算法
        size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
    
        // 位域
        // 0000 000000000 00000000000 0000 000  1
        
        dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
        idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;
    
        dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
        idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;
    
        dqai.dqai_relpri = -(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
        idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;
    
        dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
        idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;
    
        dqai.dqai_autorelease_frequency =
                idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
        idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
    
        dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
        idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
    
        return dqai;
    }
    

    第一句代码就是将我们的外界传进来区分串行还是并发的参数传进去创建了一个 dispatch_queue_attr_info_t 类型的结构体。
    一查 dispatch_queue_attr_info_t 是一个结构体位域,结构体位域可以通过一些位运算取出我们想要的内容,过滤掉我们不想要的数据。

    1.串行队列传的是NULL ,所以直接返回.
    2.并行队列,通过按位取余设置下面的各个参数

    typedef struct dispatch_queue_attr_info_s {
        dispatch_qos_t dqai_qos : 8;
        int      dqai_relpri : 8;
        uint16_t dqai_overcommit:2;
        uint16_t dqai_autorelease_frequency:2;
        uint16_t dqai_concurrent:1;
        uint16_t dqai_inactive:1;
    } dispatch_queue_attr_info_t;
    
    

    回到原来的方法_dispatch_lane_create_with_target继续分析

    static dispatch_queue_t
    _dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
            dispatch_queue_t tq, bool legacy)
    {
        ////其实就是把attr转化为{}字典形式的attr集合,dqa里面会有qos,overcommit,inactive,concurrent之类的key和value
        dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
    
        //
        // Step 1: Normalize arguments (qos, overcommit, tq)
        //
    
        dispatch_qos_t qos = dqai.dqai_qos;
    #if !HAVE_PTHREAD_WORKQUEUE_QOS
        if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
            dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
        }
        if (qos == DISPATCH_QOS_MAINTENANCE) {
            dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
        }
    #endif // !HAVE_PTHREAD_WORKQUEUE_QOS
    
        _dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
        if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
            if (tq->do_targetq) {
                DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
                        "a non-global target queue");
            }
        }
    
        if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
            // Handle discrepancies between attr and target queue, attributes win
            if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
                if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
                    overcommit = _dispatch_queue_attr_overcommit_enabled;
                } else {
                    overcommit = _dispatch_queue_attr_overcommit_disabled;
                }
            }
            if (qos == DISPATCH_QOS_UNSPECIFIED) {
                qos = _dispatch_priority_qos(tq->dq_priority);
            }
            tq = NULL;
        } else if (tq && !tq->do_targetq) {
            // target is a pthread or runloop root queue, setting QoS or overcommit
            // is disallowed
            if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
                DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
                        "and use this kind of target queue");
            }
        } else {
            if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
                // Serial queues default to overcommit!
                overcommit = dqai.dqai_concurrent ?
                        _dispatch_queue_attr_overcommit_disabled :
                        _dispatch_queue_attr_overcommit_enabled;
            }
        }
        if (!tq) {
            tq = _dispatch_get_root_queue(
                    qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
                    overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
            if (unlikely(!tq)) {
                DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
            }
        }
    
        //
        // Step 2: Initialize the queue
        //
    
        if (legacy) {
            // if any of these attributes is specified, use non legacy classes
            if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
                legacy = false;
            }
        }
    
        const void *vtable;
        dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
        if (dqai.dqai_concurrent) {
            // 通过dqai.dqai_concurrent 来区分并发和串行
            // OS_dispatch_queue_concurrent_class
            vtable = DISPATCH_VTABLE(queue_concurrent);
        } else {
            vtable = DISPATCH_VTABLE(queue_serial);
        }
        switch (dqai.dqai_autorelease_frequency) {
        case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
            dqf |= DQF_AUTORELEASE_NEVER;
            break;
        case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
            dqf |= DQF_AUTORELEASE_ALWAYS;
            break;
        }
        if (label) {
            const char *tmp = _dispatch_strdup_if_mutable(label);
            if (tmp != label) {
                dqf |= DQF_LABEL_NEEDS_FREE;
                label = tmp;
            }
        }
        
        // 开辟内存 - 生成响应的对象 queue
        dispatch_lane_t dq = _dispatch_object_alloc(vtable,
                sizeof(struct dispatch_lane_s));
        
        // 构造方法
        _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_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;
        }
        if (!dqai.dqai_inactive) {
            _dispatch_queue_priority_inherit_from_target(dq, tq);
            _dispatch_lane_inherit_wlh_from_target(dq, tq);
        }
        _dispatch_retain(tq);
        dq->do_targetq = tq;
        _dispatch_object_debug(dq, "%s", __func__);
        return _dispatch_trace_queue_create(dq)._dq;
    }
    

    通过dqai.dqai_concurrent 来区分并发和串行。_dispatch_queue_attr_overcommit_enabled串行,_dispatch_queue_attr_overcommit_disabled并发

    overcommit = dqai.dqai_concurrent ?
                        _dispatch_queue_attr_overcommit_disabled :
                        _dispatch_queue_attr_overcommit_enabled;
    

    dispatch_object_t

    这里通过_dispatch_object_alloc开辟内存,那我们看一下dispatch_object_t的内容。
    一般通过继承实现多态,这里通过联合体的方式,包含了很多类型,避免开辟太多空间,是互斥的,同一时间只能有一个有效

    typedef struct dispatch_object_s {
    private:
        dispatch_object_s();
        ~dispatch_object_s();
        dispatch_object_s(const dispatch_object_s &);
        void operator=(const dispatch_object_s &);
    } *dispatch_object_t;
    #define DISPATCH_DECL(name) \
            typedef struct name##_s : public dispatch_object_s {} *name##_t
    #define DISPATCH_DECL_SUBCLASS(name, base) \
            typedef struct name##_s : public base##_s {} *name##_t
    #define DISPATCH_GLOBAL_OBJECT(type, object) (static_cast<type>(&(object)))
    #define DISPATCH_RETURNS_RETAINED
    #else /* Plain C */
    #ifndef __DISPATCH_BUILDING_DISPATCH__
    typedef union {
        struct _os_object_s *_os_obj;
        struct dispatch_object_s *_do;
        struct dispatch_queue_s *_dq;
        struct dispatch_queue_attr_s *_dqa;
        struct dispatch_group_s *_dg;
        struct dispatch_source_s *_ds;
        struct dispatch_mach_s *_dm;
        struct dispatch_mach_msg_s *_dmsg;
        struct dispatch_semaphore_s *_dsema;
        struct dispatch_data_s *_ddata;
        struct dispatch_io_s *_dchannel;
    } dispatch_object_t DISPATCH_TRANSPARENT_UNION;
    

    这里我们看一个例子。

    // com.apple.root.default-qos width = 0xffe
        // com.apple.root.default-qos.overcommit   width = 0x1 = 1
        // 队列 - 模板 _dispatch_root_queues里面取值
    //    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
    //        .dq_label = "com.apple.root.default-qos",
    //        .dq_serialnum = 10,
    //    ),
    //    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
    //            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
    //        .dq_label = "com.apple.root.default-qos.overcommit",
    //        .dq_serialnum = 11,
    //    ),
    
        // dispatch_init
        // _dispatch_root_queues init
        // width = 0xfff
        // 自定义: 0xffe
    dispatch_queue_t queue1 = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_queue_t queue2 = dispatch_queue_create("KC", NULL);
        
        NSLog(@"%@",dispatch_get_main_queue());
        
        NSLog(@"%@",dispatch_get_global_queue(0, 0));
    
        NSLog(@"%@-%@",queue1,queue2);
    

    打印结果如下

    2020-08-23 22:47:45.560834+0800 001---函数与队列[95788:1954758] 来了 master
    2020-08-23 22:47:45.561110+0800 001---函数与队列[95788:1954758] <OS_dispatch_queue_main: com.apple.main-thread>
    2020-08-23 22:47:45.561291+0800 001---函数与队列[95788:1954758] <OS_dispatch_queue_global: com.apple.root.default-qos>
    2020-08-23 22:47:45.561472+0800 001---函数与队列[95788:1954758] <OS_dispatch_queue_concurrent: cooci>-<OS_dispatch_queue_serial: KC>
    (lldb) po queue1
    <OS_dispatch_queue_concurrent: cooci[0x600003686600] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos[0x105530f00], width = 0xffe, state = 0x0000041000000000, in-flight = 0}>
    
    (lldb) po quere2
    error: use of undeclared identifier 'quere2'
    (lldb) po queue2
    <OS_dispatch_queue_serial: KC[0x600003686700] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos.overcommit[0x105530f80], width = 0x1, state = 0x001ffe2000000000, in-flight = 0}>
    

    通过上面分析我们可以看到一个com.apple.root.default-qos,width = 0xffe,为并发队列,一个com.apple.root.default-qos.overcommit,width = 0x1 = 1为串行队列。DISPATCH_QUEUE_WIDTH_MAX是通过源码知道是一个宏定义,等于0x1000减2,结果为0xffe。中间有一个宏DISPATCH_QUEUE_WIDTH_POOL 为0x1000减一,所以这里是减2。


    屏幕快照 2020-09-22 下午10.25.32.png
    // 构造方法
        _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
                DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
                (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
    

    _dispatch_get_root_queue

    static inline dispatch_queue_global_t
    _dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
    {
        if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
            DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
        }
        // 4-1= 3
        // 2*3+0/1 = 6/7
        return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
    }
    

    通过上面的源码可以看到_dispatch_get_root_queue方法的传值,qos为4,overcommit为0,1,串行为1,并发为0,默认为1。

    if (!tq) {
            tq = _dispatch_get_root_queue(
                    qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
                    overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
            if (unlikely(!tq)) {
                DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
            }
        }
    

    这里其实是return _dispatch_root_queues[2 * (qos - 1) + overcommit]
    也就是说target queue就是从_dispatch_root_queues队列里面拿第(2 * (qos - 1) + overcommit)个queue。
    我们接下来看下_dispatch_root_queues这个数组,在源码里查找

    struct dispatch_queue_global_s _dispatch_root_queues[] = {
    #define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
            ((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
            DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
            DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
    #define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
        [_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
            DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
            .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
            .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
            .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
            .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                    _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
                    _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
            __VA_ARGS__ \
        }
        _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
            .dq_label = "com.apple.root.maintenance-qos",
            .dq_serialnum = 4,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
            .dq_label = "com.apple.root.maintenance-qos.overcommit",
            .dq_serialnum = 5,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
            .dq_label = "com.apple.root.background-qos",
            .dq_serialnum = 6,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
            .dq_label = "com.apple.root.background-qos.overcommit",
            .dq_serialnum = 7,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
            .dq_label = "com.apple.root.utility-qos",
            .dq_serialnum = 8,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
            .dq_label = "com.apple.root.utility-qos.overcommit",
            .dq_serialnum = 9,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
            .dq_label = "com.apple.root.default-qos",
            .dq_serialnum = 10,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
                DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
            .dq_label = "com.apple.root.default-qos.overcommit",
            .dq_serialnum = 11,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
            .dq_label = "com.apple.root.user-initiated-qos",
            .dq_serialnum = 12,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
            .dq_label = "com.apple.root.user-initiated-qos.overcommit",
            .dq_serialnum = 13,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
            .dq_label = "com.apple.root.user-interactive-qos",
            .dq_serialnum = 14,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
            .dq_label = "com.apple.root.user-interactive-qos.overcommit",
            .dq_serialnum = 15,
        ),
    };
    

    这里就拿到了上面的打印结果。

    打印

        NSLog(@"%@",dispatch_get_main_queue());
        
        NSLog(@"%@",dispatch_get_global_queue(0, 0));
    

    输出结果为

    2020-08-27 20:38:34.511876+0800 001---函数与队列[96868:2126481] <OS_dispatch_queue_main: com.apple.main-thread>
    2020-08-27 20:38:34.512160+0800 001---函数与队列[96868:2126481] <OS_dispatch_queue_global: com.apple.root.default-qos>
    (lldb) po dispatch_get_global_queue(0, 0)
    <OS_dispatch_queue_global: com.apple.root.default-qos[0x109113f00] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
    

    dispatch_get_main_queue()组队列可以直接输出,是个全局静态结构体,源码搜索com.apple.main-thread,发现_dispatch_main_q如下,dq_serialnum为1是串行队列

    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,
    };
    

    这里有个宏DISPATCH_GLOBAL_OBJECT_HEADER,搜下看相当于这四句代码

    #if OS_OBJECT_HAVE_OBJC1
    #define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
        .do_vtable = DISPATCH_VTABLE(name), \
        ._objc_isa = DISPATCH_OBJC_CLASS(name), \
        .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
        .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
    #else
    #define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
        .do_vtable = DISPATCH_VTABLE(name), \
        .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
        .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
    #endif
    

    继续往下分析,打印dispatch_get_global_queue(0, 0)发现它的width = 0xfff,也就是0x1000减1,给了全局并发队列
    // dispatch_init
    // _dispatch_root_queues init 初始化就是0xfff,为了区分自定义的,自定义的是0xffe,任何队列都是由_dispatch_root_queues进行模板创建的,除去main_queue。
    // width = 0xfff
    // 自定义: 0xffe

    总结一下各种queue的获取方式吧:
    自己create创建的queue是需要alloc分配内存以后init,最后从root_queue里面的拿一个作为新queue的target queue的
    main和global queue是不需要alloc init的,直接从root_queue里拿出对应的queue即可

    相关文章

      网友评论

          本文标题:iOS 底层原理 - GCD

          本文链接:https://www.haomeiwen.com/subject/dnrjjktx.html