GCD裡的全域性佇列

weixin_34236869發表於2018-09-20

先說一下我們要用到的東西。
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呼叫就可以了。先說第一個引數。

5140207-de09fa883b66ec58.png
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());
5140207-4e664a128696ee3c.png
螢幕快照 2018-09-20 下午5.45.44.png

相關文章