美文网首页
GCD里的全局队列

GCD里的全局队列

作者: 527267线的iOS工程师 | 来源:发表于2018-09-20 17:46 被阅读0次

    先说一下我们要用到的东西。
    dispatch_queue_s里有个属性叫dq_serialnum。意义如名:序号。。。
    overcommit:带有 overcommit 的队列表示每当有任务提交时,系统都会新开一个线程处理,这样就不会造成某个线程过载(overcommit)。
    源码里一共定义了15个。

    DISPATCH_CACHELINE_ALIGN
    struct dispatch_queue_s _dispatch_main_q = {
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
    #if !DISPATCH_USE_RESOLVERS
        .do_targetq = &_dispatch_root_queues[
                DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT],
    #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_CANNOT_TRYSYNC | DQF_WIDTH(1),
        .dq_serialnum = 1,
    };
    DISPATCH_CACHELINE_ALIGN
    struct dispatch_queue_s _dispatch_mgr_q = {
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_mgr),
        .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
                DISPATCH_QUEUE_ROLE_BASE_ANON,
        .do_targetq = &_dispatch_mgr_root_queue,
        .dq_label = "com.apple.libdispatch-manager",
        .dq_atomic_flags = DQF_WIDTH(1),
        .dq_priority = DISPATCH_PRIORITY_FLAG_MANAGER |
                DISPATCH_PRIORITY_SATURATED_OVERRIDE,
        .dq_serialnum = 2,
    };
    static struct dispatch_queue_s _dispatch_mgr_root_queue = {
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_root),
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE,
        .do_ctxt = &_dispatch_mgr_root_queue_context,
        .dq_label = "com.apple.root.libdispatch-manager",
        .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL),
        .dq_priority = DISPATCH_PRIORITY_FLAG_MANAGER |
                DISPATCH_PRIORITY_SATURATED_OVERRIDE,
        .dq_serialnum = 3,
    };
    DISPATCH_CACHELINE_ALIGN
    struct dispatch_queue_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_root), \
            .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
            .do_ctxt = &_dispatch_root_queue_contexts[ \
                    _DISPATCH_ROOT_QUEUE_IDX(n, flags)], \
            .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
            .dq_priority = _dispatch_priority_make(DISPATCH_QOS_##n, 0) | flags | \
                    DISPATCH_PRIORITY_FLAG_ROOTQUEUE | \
                    ((flags & DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE) ? 0 : \
                    DISPATCH_QOS_##n << DISPATCH_PRIORITY_OVERRIDE_SHIFT), \
            __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_DEFAULTQUEUE,
            .dq_label = "com.apple.root.default-qos",
            .dq_serialnum = 10,
        ),
        _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
                DISPATCH_PRIORITY_FLAG_DEFAULTQUEUE | 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,
        ),
    };
    

    这些全局队列都是由系统创建的。我们只需要dispatch_get_global_queue调用就可以了。先说第一个参数。

    image.png

    //表是转的。如果有什么问题。求轻骂。
    系统的命名很人性化。是根据OS_ENUM给的命名。

    __QOS_ENUM(qos_class, unsigned int,
        QOS_CLASS_USER_INTERACTIVE
                __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x21,
        QOS_CLASS_USER_INITIATED
                __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x19,
        QOS_CLASS_DEFAULT
                __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x15,
        QOS_CLASS_UTILITY
                __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x11,
        QOS_CLASS_BACKGROUND
                __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x09,
        QOS_CLASS_UNSPECIFIED
                __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x00,
    );
    

    1号为主队列。2.3为管理队列。2号的do_targetq为3号。剩下的和枚举值对应。
    对应的枚举。
    QOS_CLASS_USER_INTERACTIVE:要求此类工作相对于系统上的其他工作以高优先级运行。指定这个QOS类是在几乎所有可用系统CPU和I/O带宽处于争用状态下运行的请求。这不是用于大型任务的节能QOS类。这个QOS类的使用应该仅限于与用户的关键交互,例如在主事件循环中处理事件、视图绘制、动画等。我们可以看出主线程对应的是QOS_CLASS_USER_INTERACTIVE。
    QOS_CLASS_USER_INITIATED:这类工作的优先级要求低于关键的用户交互工作,但相对高于系统上的其他工作。这不是用于大型任务的节能QOS类。它的使用应该限制在足够短的持续时间内,这样用户在等待结果时就不太可能切换任务。典型的用户发起的工作将通过显示占位符内容或模态用户界面来指示进度。dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)获取的就是QOS_CLASS_USER_INITIATED的。
    QOS_CLASS_DEFAULT:这类工作的优先级要求低于关键用户交互和用户发起的工作,但相对高于实用程序和后台任务。pthread_create()创建的线程如果没有指定QOS类的属性,则默认为QOS_CLASS_DEFAULT。qos_class是可以设置pthread优先级的。此QOS类值不打算用作工作分类,仅在传播或恢复系统提供的QOS类值时设置。我们用的比较多。
    QOS_CLASS_UTILITY:这类工作的优先级要求低于关键用户交互和用户发起的工作,但相对高于低级系统维护任务。使用这个QOS类表明工作应该以一种能量和热效率的方式运行。效用工作的进展可能会或不会向用户显示,但这种工作的效果是用户可见的。对应的是我们常用的DISPATCH_QUEUE_PRIORITY_LOW。
    QOS_CLASS_BACKGROUND:要求这些工作优先于其他工作运行。使用这个QOS类表明工作应该以最节能和最高效的方式运行。
    4.5号我不能在app项目里使用到。希望有知道的大佬告诉我。
    我们一般获取全局队列都用方法dispatch_get_global_queue。两个参数。第一个参数是优先级的。和上面对应。所以qos_class和DISPATCH_QUEUE_PRIORITY_HIGH都可以使用。第二个参数是用来控制是否overcommit.
    上源码:

    dispatch_queue_t
    dispatch_get_global_queue(long priority, unsigned long flags)
    {
        if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
            return DISPATCH_BAD_INPUT;
        }
        dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
    #if !HAVE_PTHREAD_WORKQUEUE_QOS
        if (qos == QOS_CLASS_MAINTENANCE) {
            qos = DISPATCH_QOS_BACKGROUND;
        } else if (qos == QOS_CLASS_USER_INTERACTIVE) {
            qos = DISPATCH_QOS_USER_INITIATED;
        }
    #endif
        if (qos == DISPATCH_QOS_UNSPECIFIED) {
            return DISPATCH_BAD_INPUT;
        }
        return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
    }
    static inline dispatch_queue_t
    _dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
    {
        if (unlikely(qos == DISPATCH_QOS_UNSPECIFIED || qos > DISPATCH_QOS_MAX)) {
            DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
        }
        return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
    }
    

    这里可以看到一个判断

    #define HAVE_PTHREAD_WORKQUEUE_QOS 1
    #if !HAVE_PTHREAD_WORKQUEUE_QOS
        if (qos == QOS_CLASS_MAINTENANCE) {
            qos = DISPATCH_QOS_BACKGROUND;
        } else if (qos == QOS_CLASS_USER_INTERACTIVE) {
            qos = DISPATCH_QOS_USER_INITIATED;
        }
    

    可以为我们解决一点上面的疑问。//我很好奇他这个写法。求教为什么这么写
    在这里flags & DISPATCH_QUEUE_OVERCOMMIT作为一个bool传给_dispatch_get_root_queue。
    enum {
    DISPATCH_QUEUE_OVERCOMMIT = 0x2ull,
    };
    这个ull就是unsigned long long。所以我们给传一个2的话。就可以拿到有OVERCOMMIT的队列。
    最后我的测试代码

        NSLog(@"high%@", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
        NSLog(@"OVERCOMMIT%@", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
        NSLog(@"DEFAULT%@", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
        NSLog(@"LOW%@", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
        NSLog(@"INITIATED%@", dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 2));
        NSLog(@"BACKGROUND%@", dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 2));
        NSLog(@"DEFAULT%@", dispatch_get_global_queue(QOS_CLASS_DEFAULT, 2));
        NSLog(@"UTILITY%@", dispatch_get_global_queue(QOS_CLASS_UTILITY, 2));
        NSLog(@"%@", dispatch_get_main_queue());
    
    屏幕快照 2018-09-20 下午5.45.44.png

    相关文章

      网友评论

          本文标题:GCD里的全局队列

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