英文:
Using "withTaskGroup" and "async let" together
问题
以下是我对你提供的内容的翻译:
这是我如何使用withTaskGroup
的简化版本:
let allResults = await withTaskGroup(of: ([Double], Float, Float, Int).self,
returning: FinalResult.self,
body: { taskGroup in
for operation in operations {
taskGroup.addTask { @MainActor in
let value = await operation.execute()
return value
}
}
// 在这里收集子任务的结果...
})
execute
函数如下所示,其中包括针对机器学习模型的特征提取等数学计算密集型任务:
func execute() async -> ([Double], Float, Float, Int) {
async let result0: [Double] = process0() ?? []
async let result1: Float = process1() ?? -1
async let result2: Float = process2()
async let result3: Int = process3()
return (await result0, await result1, await result2, await result3)
}
我遇到的问题是最终结果不一致,有时候可以得到正确的结果,有时候不行。例如,有时候我得到一个包含512个特征(数组长度)的全零数组,而它们应该是非零数字。相反,如果我只是使用for循环
而不是withTaskGroup
,结果似乎更加一致。
因此,我想知道在使用withTaskGroup
和async let
结合使用时是否存在任何后果,因为这是一种并行的并行操作。与并发不同,我理解并行性是将每个CPU核心分配给同时处理任务,但如果我提供了4个operations
,这意味着有4 x 4 = 16个并行进程,这多于可用核心数。
英文:
Following is a simplified version of how I'm using withTaskGroup
:
let allResults = await withTaskGroup(of: ([Double], Float, Float, Int).self,
returning: FinalResult.self,
body: { taskGroup in
for operation in operations {
taskGroup.addTask { @MainActor in
let value = await operation.execute()
return value
}
}
// Collect child task results here...
})
The execute
function looks as follows where the processes include fairly math calculation intensive tasks such as feature extractions for a machine learning model:
func execute() async -> ([Double], Float, Float, Int) {
async let result0: [Double] = process0() ?? []
async let result1: Float = process1() ?? -1
async let result2: Float = process2()
async let result3: Int = process3()
return (await result0, await result1, await result2, await result3)
}
The issue I'm experiencing is that the final result is inconsistent where sometimes I get proper results and sometimes not. An example would be that sometimes I get an array of all zeros for 512 features (array length) when they're supposed to be non zero numbers. Contrarily, if I were to simply use a for loop
instead of withTaskGroup
, the result seems to be a bit more consistent.
So I'm wondering if there are any consequences of using withTaskGroup
and async let
together since this is a parallel of a parallel. Unlike concurrency, my understanding of parallelism is that each core of CPU is assigned to process the tasks simultaneously, but if I were to provide 4 operations
, that means 4 x 4 = 16 parallel processes, which is more than the number of available cores.
答案1
得分: 1
这绝对不是问题。问题更有可能是您在process1()
等函数中的代码不是线程安全的(例如依赖于从不是线程安全的属性中读取的数据,或以无效的方式操作返回的数组)。如果您已经正确编写了代码,那么超出核心数不会引起逻辑问题。它可能会引起性能问题,具体取决于许多因素,但不会导致正确性问题。
您可能希望在构建设置中启用“严格并发检查”,这可以帮助您找到错误。 (它还可以找到Foundation中的许多问题,您无法修复,因此最好在孤立的代码或较小的项目上使用它。)
英文:
This is definitely not the problem. The problem is much more likely that your code in process1()
, etc., is not thread-safe (relying on data read from properties that aren't thread-safe, for example, or manipulating the returned Array in an invalid way). Exceeding the number of cores is not going to cause a logic problem if you've otherwise written the code correctly. It can cause performance problems, depending on many factors, but it will not cause problems with correctness.
You may want to turn on "Strict Concurrency Checking" in your build settings, which can help you find mistakes. (It can also find a lot of issues in Foundation which you can't fix, so it's sometimes best to use it on isolated code or a smaller project.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论