iOS GCD

队列

概念

  • 按创建主体来划分
    • 默认队列
    • 自定义队列,自定义队列通过target指定放到哪个默认队列,不指定时放在DEFAULT队列
  • 按执行线程来划分:
    • main queue 只会在主线程执行
    • 其他队列在线程池执行(不区分穿行队列和并发队列)
  • 按优先级来划分:
    • 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

并发队列

试验: 队列优先级与线程的映射

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

结论

  • 优先级是绝对概念,优先级高的会先于优先级低的先执行,内部实现中也能看到实现queue_drain,并不存在用户态轮转选择


  • 不同优先级的队列对应同一组线程,会在耗尽高优先级的任务之后才会消费低优先级的任务,上面的例子打印为 0...1...2...3...

  • 切换优先级时,会改变线程优先级,线程优先级改变有两种原因:

    • HIGH和DEFAULT是相对优先级,刚开始时线程优先级为37,100ms后降到36,每执行约10ms再降低1,降低到28后再次回到37,继续循环;但是LOW和BACKGROUD优先级分别对应固定的线程优先级20和4


    该机制是用于防止有人滥用高QoS,如果时长时任务,那不该是高QoS

    • 线程由执行不同的优先级队列时切换
  • 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的先执行
  • 不同QoS的队列对应同一组线程,会在耗尽高QoS的任务之后才会消费低优先级的任务,上面的例子打印为 0...1...2...3...
  • 5个QoS对应的线程优先级为:47 37 31 20 4
  • 切换不同QoS的任务开始执行时,会改变线程优先级
  • 前3个QoS会使用大核,其他只使用小核

串行队列

试验:串行队列和并发队列共存时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创建10个队列 + 提交300任务,平均任务提交时间不到1us,含唤醒其他线程

  • 串行队列和并发队列是否公用线程池

线程唤醒

策略

时延

线程被任务阻塞

如果用户的任务阻塞了线程池,GCD如何处理

并发队列的阻塞

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

周期性孤立任务是否触发逃生

[图片上传失败...(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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一:base.h 二:block.h 1. dispatch_block_flags:DISPATCH_BLOCK...
    小暖风阅读 7,561评论 0 0
  • 总结的很到位,附上版权声明: https://blog.csdn.net/wei371522/article/de...
    FMG阅读 4,857评论 0 0
  • 说起GCD大家都不陌生了,就是为了应付面试,也需要把GCD的功能和作用给背个滚瓜烂熟,在此,就不再赘述所谓的作用了...
    莽原奔马668阅读 8,898评论 1 14
  • GCD,全称是 Grand Central Dispatch,纯 C 语言,提供了非常多强大的函数. 是苹果公司为...
    Clark_new阅读 2,593评论 0 2
  • 前言 GCD在我们的实际开发中,用到非常之我,那么它的原理是怎么样的,我们来分析下。 GCD概念 全称是Grand...
    似水流年_9ebe阅读 3,712评论 0 3

友情链接更多精彩内容