iOS GCD

隊列

概念

  • 按創(chuàng)建主體來劃分
    • 默認隊列
    • 自定義隊列,自定義隊列通過target指定放到哪個默認隊列,不指定時放在DEFAULT隊列
  • 按執(zhí)行線程來劃分:
    • main queue 只會在主線程執(zhí)行
    • 其他隊列在線程池執(zhí)行(不區(qū)分穿行隊列和并發(fā)隊列)
  • 按優(yōu)先級來劃分:
    • DISPATCH_QUEUE_PRIORITY_HIGH 2
    • DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    • DISPATCH_QUEUE_PRIORITY_LOW (-2)
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
  • 按QoS來劃分:
    • QOS_CLASS_USER_INTERACTIVE
    • QOS_CLASS_USER_INITIATED
    • QOS_CLASS_DEFAULT
    • QOS_CLASS_UTILITY
    • QOS_CLASS_BACKGROUND

并發(fā)隊列

試驗: 隊列優(yōu)先級與線程的映射

static uint64_t sum = 0;
template <auto tag>
static void stall(void* c)
{
    auto start = now();
    while (time_us(start) < 100000) sum++;
    usleep(10);
    os_log(os_log_create("com.yourapp", "stall"), "tag: %{public}d", (int)tag);
}

int main() {
        auto g = dispatch_group_create();
        auto q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        auto q1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        auto q2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
        auto q3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
        
        auto start = now();
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q, nullptr, stall<0>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q1, nullptr, stall<1>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q2, nullptr, stall<2>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q3, nullptr, stall<3>);
        }
        dispatch_group_wait(g, DISPATCH_TIME_FOREVER);

        printf("total takes %llu us\n", time_us(start));
        
        dispatch_release(q);
    }

結論

  • 優(yōu)先級是絕對概念,優(yōu)先級高的會先于優(yōu)先級低的先執(zhí)行,內部實現(xiàn)中也能看到實現(xiàn)queue_drain,并不存在用戶態(tài)輪轉選擇


  • 不同優(yōu)先級的隊列對應同一組線程,會在耗盡高優(yōu)先級的任務之后才會消費低優(yōu)先級的任務,上面的例子打印為 0...1...2...3...

  • 切換優(yōu)先級時,會改變線程優(yōu)先級,線程優(yōu)先級改變有兩種原因:

    • HIGH和DEFAULT是相對優(yōu)先級,剛開始時線程優(yōu)先級為37,100ms后降到36,每執(zhí)行約10ms再降低1,降低到28后再次回到37,繼續(xù)循環(huán);但是LOW和BACKGROUD優(yōu)先級分別對應固定的線程優(yōu)先級20和4


    該機制是用于防止有人濫用高QoS,如果時長時任務,那不該是高QoS

    • 線程由執(zhí)行不同的優(yōu)先級隊列時切換
  • HIGH 和DEFAULT會使用大核,其他只使用小核

試驗: 隊列QoS與線程的映射關系

int main() {
        auto g = dispatch_group_create();
        auto q = dispatch_queue_create("q", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INTERACTIVE, 0));
        auto q1 = dispatch_queue_create("q1", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
        auto q2 = dispatch_queue_create("q2", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_DEFAULT, 0));
        auto q3 = dispatch_queue_create("q3", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_UTILITY, 0));
        auto q4 = dispatch_queue_create("q4", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0));
        
        auto start = now();
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q4, nullptr, stall<4>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q3, nullptr, stall<3>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q2, nullptr, stall<2>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q1, nullptr, stall<1>);
        }
        for (int i = 0; i < 50; i++) {
            dispatch_group_async_f(g, q, nullptr, stall<0>);
        }
        dispatch_group_wait(g, DISPATCH_TIME_FOREVER);

        printf("total takes %llu us\n", time_us(start));
        
        dispatch_release(q);
}

結論

  • QoS也是絕對概念,高QoS的會先于低QoS的先執(zhí)行
  • 不同QoS的隊列對應同一組線程,會在耗盡高QoS的任務之后才會消費低優(yōu)先級的任務,上面的例子打印為 0...1...2...3...
  • 5個QoS對應的線程優(yōu)先級為:47 37 31 20 4
  • 切換不同QoS的任務開始執(zhí)行時,會改變線程優(yōu)先級
  • 前3個QoS會使用大核,其他只使用小核

串行隊列

試驗:串行隊列和并發(fā)隊列共存時worker的映射關系

        auto g = dispatch_group_create();
        auto start = now();
        {
            auto q = dispatch_queue_create("q", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INTERACTIVE, 0));
            auto q1 = dispatch_queue_create("q1", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
            auto q2 = dispatch_queue_create("q2", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_DEFAULT, 0));
            auto q3 = dispatch_queue_create("q3", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_UTILITY, 0));
            auto q4 = dispatch_queue_create("q4", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0));
            
            auto start = now();
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q4, nullptr, stall<4>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q3, nullptr, stall<3>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q2, nullptr, stall<2>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q1, nullptr, stall<1>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q, nullptr, stall<0>);
            }
        }
        {
            auto q = dispatch_queue_create("q", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0));
            auto q1 = dispatch_queue_create("q1", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
            auto q2 = dispatch_queue_create("q2", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0));
            auto q3 = dispatch_queue_create("q3", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0));
            auto q4 = dispatch_queue_create("q4", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0));

            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q4, nullptr, stall<14>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q3, nullptr, stall<13>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q2, nullptr, stall<12>);
            }
            for (int i = 0; i < 30; i++) {
                dispatch_group_async_f(g, q1, nullptr, stall<11>);
            }
            for (int i = 0; i < 50; i++) {
                dispatch_group_async_f(g, q, nullptr, stall<10>);
            }
        }
        dispatch_group_wait(g, DISPATCH_TIME_FOREVER);


246 us創(chuàng)建10個隊列 + 提交300任務,平均任務提交時間不到1us,含喚醒其他線程

  • 串行隊列和并發(fā)隊列是否公用線程池

線程喚醒

策略

時延

線程被任務阻塞

如果用戶的任務阻塞了線程池,GCD如何處理

并發(fā)隊列的阻塞

void test2() {
            auto g = dispatch_group_create();
            auto q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); //("test", DISPATCH_QUEUE_CONCURRENT);
            auto q1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
            auto q2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

            auto start = now();
            for (int i = 0; i < 100; i++) {
                dispatch_group_async_f(g, q, nullptr, stall1);
            }
            dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
            for (int i = 0; i < 100; i++) {
                dispatch_group_async_f(g, q1, nullptr, stall1);
            }
            dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
            for (int i = 0; i < 100; i++) {
                dispatch_group_async_f(g, q2, nullptr, stall1);
            }
            dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
 
            printf("total takes %llu us\n", time_us(start));
            
            dispatch_release(q);
}

周期性孤立任務是否觸發(fā)逃生

[圖片上傳失敗...(image-c26c86-1748325119324)]

多進程共享線程池

參考

so逆向

dispatch.dylib
https://f.8tool.club/d.html?d=DB9B53765916340002515929456CF762

dispatch.dylib symbol
https://f.8tool.club/d.html?d=A237C5FF061A5688EAE0FC3AE61FA730

dispatch.dylib symbol
https://f.8tool.club/d.html?d=C6F3CB0F10EA48CAE6C3CE38B7034ED4

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 一:base.h 二:block.h 1. dispatch_block_flags:DISPATCH_BLOCK...
    小暖風閱讀 2,771評論 0 0
  • 總結的很到位,附上版權聲明: https://blog.csdn.net/wei371522/article/de...
    FMG閱讀 1,243評論 0 0
  • 說起GCD大家都不陌生了,就是為了應付面試,也需要把GCD的功能和作用給背個滾瓜爛熟,在此,就不再贅述所謂的作用了...
    莽原奔馬668閱讀 3,625評論 1 14
  • GCD,全稱是 Grand Central Dispatch,純 C 語言,提供了非常多強大的函數(shù). 是蘋果公司為...
    Clark_new閱讀 352評論 0 2
  • 前言 GCD在我們的實際開發(fā)中,用到非常之我,那么它的原理是怎么樣的,我們來分析下。 GCD概念 全稱是Grand...
    似水流年_9ebe閱讀 641評論 0 3

友情鏈接更多精彩內容