我想第一次尝试并发处理数组。 编译器给出以下警告:“并发执行代码中捕获的 var 'scaledImage' 发生突变;这是 Swift 6 中的错误”。
然后我通过将源数组复制为局部 let 变量来修改代码;因此,所有数据现在都是本地的,但警告仍然存在。 由于所有数据现在都是本地的,并且数组“scaledImage”是一个“接收器”数组,我不明白为什么警告仍然存在,也不明白该怎么办。
// Concurrent processing 1st attempt
func processArrayConcurrently() async -> [UInt8] {
var scaledImage = [UInt8]()
scaledImage.reserveCapacity(self.backingImage!.count)
// constants for the data
let dMin = Double(self.backingMinMax[0])
let dRange = Double(self.backingRange)
let backingImage = self.backingImage!
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
DispatchQueue.concurrentPerform(iterations: backingImage.count) { index in
let processedValue = UInt8(backingImage[index] * 2) // Example processing
concurrentQueue.async(flags: .barrier) { // WARNING next line
scaledImage[index] = processedValue
}
}
// Wait for all tasks to complete
concurrentQueue.sync(flags: .barrier) {}
return scaledImage
}
这里的警告是因为 Swift Concurrency 不理解 GCD 的所有语义。在这种情况下,它不明白
.barrier
的含义。
即使
scaledImage
是局部变量,它也会被捕获在 @Sendable
闭包中(闭包传递给 concurrentQueue.async(flags: .barrier)
),此时它就变成共享状态。闭包可以在不同的线程之间传递,就 Swift 并发而言,各种各样的事情都可能发生。
底线是,你不应该将 Swift Concurrency 与 GCD 混合在一起。如果你想使用GCD,删除
async
,警告就会消失。也就是说,您的代码中还存在其他错误。 reserveCapacity
实际上并没有向数组添加任何内容,因此所有数组访问都将越界。看起来你想在 concurrentQueue
上进行所有处理,但是 DispatchQueue.concurrentPerform
运行在 current 调度队列上,这绝对不是 concurrentQueue
。
如果你想使用 Swift Concurrency,你应该使用
TaskGroup
。
func processArrayConcurrently() async -> [UInt8] {
let dMin = Double(self.backingMinMax[0])
let dRange = Double(self.backingRange)
let backingImage = self.backingImage
return await withTaskGroup(of: (index: Int, image: UInt8).self) { group in
var scaledImage = [UInt8](repeating: 0, count: backingImage.count)
for (i, image) in backingImage.enumerated() {
group.addTask {
(i, UInt8(image * 2)) // do some processing
}
}
for await (i, image) in group {
scaledImage[i] = image
}
return scaledImage
}
}
此方法创建一个任务组,其子任务结果类型为
(Int, UInt8)
。 Int
用于记录应插入 scaledImage
(实际结果)的 UInt8
的索引。
只要
processArrayConcurrently
不与任何参与者隔离,处理就会在后台线程中运行。