在 Swift 中同时访问多个可变视图到数组中

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

斯威夫特 5.9

我正在尝试同时对数组的一些不重叠的子范围进行排序。到目前为止我的方法看起来像这样:

// Not the real array setup, but sufficiently representative
let arraySize = 1_000_000
var someArray = [Int]()
someArray.reserveCapacity(arraySize)
for _ in 0..<arraySize {
    someArray.append(.random(in: 0...1_000_000))
}

// Let's say we want to break the array into 6 SubSequences of
// roughly equal size and sort each one
let divisions = 6
var divisionLength = arraySize / divisions
// Guarantee that the last division will be at least as large
// as the remainder of the array
if arraySize % divisions != 0 { divisionLength += 1 }

await withTaskGroup(of: Void.self) { taskGroup in
    var currentIndex = 0

    while currentIndex < someArray.endIndex {
        let sliceStart = currentIndex
        let sliceEnd = min(currentIndex + divisionLength, someArray.endIndex)
        currentIndex = sliceEnd

        taskGroup.addTask {
            // compilation error: mutation of captured var 'someArray'
            // in concurrently-executing code

            someArray[sliceStart..<sliceEnd].sort()
        }
    }

    await taskGroup.waitForAll()
}

我隐隐怀疑,由于 Swift 处理变异值类型的方式,不存在一种关于并发可变性的机制来表示“Trust Me Bro™️”。

这是基于我的理解,即使我抽象排序函数并互斥其操作的范围,CoW 也会使其他线程持有的引用无效。

不过,我想确定 - 是否存在某种并发安全覆盖可以让我做到这一点?或者,还有另一种方法吗?

arrays swift concurrency
1个回答
0
投票

写入数组总是不安全的,即使您写入数组的不同范围。虽然可以通过将数组包装在

@unchecked Sendable
类中来绕过编译器错误,

class Foo: @unchecked Sendable {
    var array = [Int]()
}

Swift 仍然阻止您在运行时同时写入。另请参阅 Swift 指南中的内存安全。我认为这里发生的事情是 Swift 将整个

someArray
算作“一个内存位置”。

要实际执行此操作,您可以通过

UnsafeMutableBufferPointer
访问数组的内部缓冲区。有
withUnsafeMutableBufferPointer
允许您执行此操作,但该方法提供了
inout
参数,您无法在异步代码中捕获该参数。

我首先会写一个包装器

withUnsafeMutableBufferPointer
:

extension Array {
    mutating func withBuffer(block: (UnsafeMutableBufferPointer<Element>) -> Void) {
        withUnsafeMutableBufferPointer { p in
            block(p)
        }
    }
}

并像这样使用它:

someArray.withBuffer { p in
    taskGroup.addTask {
        print("Started")
        p[sliceStart..<sliceEnd].sort()
        print("Ended")
    }
}

不用说,现在没有编译器检查线程安全性 - 您需要手动执行此操作。

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