Swift 并发警告。编译器警告我的并发代码无法在 Swift 6 中编译,但所有变量都是本地的

问题描述 投票:0回答:1

我想第一次尝试并发处理数组。 编译器给出以下警告:“并发执行代码中捕获的 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 xcode concurrency arm
1个回答
0
投票

这里的警告是因为 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
不与任何参与者隔离,处理就会在后台线程中运行。

© www.soinside.com 2019 - 2024. All rights reserved.