【问题标题】:Maximum number of threads with async-await task groups具有异步等待任务组的最大线程数
【发布时间】:2021-08-30 19:39:11
【问题描述】:

我的意图是了解 Swift 5.5 的 async-await 使用的“协作线程池”,以及任务组如何自动约束并发程度:考虑以下任务组代码,并行进行 32 次计算:

func launchTasks() async {
    await withTaskGroup(of: Void.self) { group in
        for i in 0 ..< 32 {
            group.addTask { [self] in
                let value = await doSomething(with: i)
                // do something with `value`
            }
        }
    }
}

虽然我希望它会限制并发程度,但正如所宣传的那样,我一次只能获得两个(!)并发任务。这比我预期的要严格得多:

如果我使用旧的 GCD concurrentPerform ...

func launchTasks2() {
    DispatchQueue.global().async {
        DispatchQueue.concurrentPerform(iterations: 32) { [self] i in
            let value = doSomething(with: i)
            // do something with `value`
        }
    }
}

...我一次得到 12 个,充分利用设备(我的 6 核 i9 MacBook Pro 上的 iOS 15 模拟器)同时避免线程爆炸:

(FWIW,这两者都在 Big Sur 上运行的 Xcode 13.0 beta 1 (13A5154h) 中进行了分析。请忽略这两次运行中各个“工作”的细微差别,因为所讨论的功能只是为一个随机的持续时间;关键的观察是并发程度是我们所期望的。)

这个新的 async-await(和任务组)自动限制并行度非常好,但是 async-await 的协作线程池比我预期的要受限制得多。而且我认为无法调整该池的这些参数。我们如何才能更好地利用我们的硬件,同时仍然避免线程爆炸(不求助于非零信号量或操作队列等旧技术)?

【问题讨论】:

    标签: ios swift async-await grand-central-dispatch swift5.5


    【解决方案1】:

    看起来这种奇怪的行为是模拟器的限制。如果我在我的物理 iPhone 12 Pro Max 上运行它,异步等待任务组方法会导致 6 个并发任务......

    ...这与concurrentPerform 行为基本相同:

    包括并发程度在内的行为在物理设备上基本相同。

    我们可以推断,模拟器似乎被配置为对 async-await 的限制超过了直接 GCD 调用所能实现的限制。但在实际的物理设备上,异步等待任务组的行为与预期的一样。

    【讨论】:

    • 按我的理解正确,新的协作线程池,与之前的不同,不会过度使用系统(每个核心不超过一个线程在运行)。这使我们能够摆脱空闲线程和不必要的上下文切换。但从你的concurrentPerform-solution 截图来看,GCD 的优化方式完全相同。是这样吗?
    • 是的,concurrentPerform 和新的协作线程池方法都限制了线程数,避免了线程爆炸。恕我直言,主要的好处是避免了当线程爆炸耗尽 GCD 非常有限的工作线程池时可能出现的死锁。
    猜你喜欢
    • 2015-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    相关资源
    最近更新 更多