斯威夫特 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 也会使其他线程持有的引用无效。
不过,我想确定 - 是否存在某种并发安全覆盖可以让我做到这一点?或者,还有另一种方法吗?
写入数组总是不安全的,即使您写入数组的不同范围。虽然可以通过将数组包装在
@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")
}
}
不用说,现在没有编译器检查线程安全性 - 您需要手动执行此操作。