我的目的是了解 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 = 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个,充分利用设备(iOS 15模拟器在我的6核i9 MacBook Pro上),同时避免线程爆炸:
顺便提一下,这两个都是在运行于Big Sur的Xcode 13.0 beta 1(13A5154h)中进行了剖析。请忽略这两次运行中各自“作业”之间的微小差异,因为所涉及的函数只是在随机时间内旋转;关键观察点是并发度是我们预期的。
这个新的async
-await
(以及任务组)自动限制了并行度,这非常棒,但是async
-await
的协作线程池比我预期的要受到更多限制。我看不到任何调整该池参数的方法。我们如何更好地利用硬件而避免线程爆炸(而不使用像非零信号量或操作队列等旧技术)?
concurrentPerform
解决方案的截图来看,GCD似乎以完全相同的方式进行了优化。是这样吗? - V.V.VconcurrentPerform
和新的协作线程池方法都限制了线程数量,避免了线程爆炸。我认为,主要的好处在于这样可以避免死锁,因为线程爆炸会耗尽 GCD 非常有限的工作线程池。 - RobconcurrentPerform
一样。回到那个旧的GCD API真的没有任何好处,而且有很多理由不这样做。话虽如此,演员是可重入的,网络请求根本不受线程限制。坦白地说,我们经常遇到相反的问题,即我们故意想将网络请求限制在4-8个,以减少峰值内存使用和最小化服务器影响。做更多的事情几乎没有性能上的好处,但会引入问题。 - Rob