我发现了一些奇怪的东西:无论出于何种原因,在以下代码运行后,数组版本几乎总是包含随机 0,而指针版本则不会。
var a = UnsafeMutablePointer<Int>.allocate(capacity: N)
//var a = [Int](repeating: 0, count: N)
let n = N / iterations
DispatchQueue.concurrentPerform(iterations: iterations) { j in
for i in max(j * n, 1)..<((j + 1) * n) {
a[i] = 1
}
}
for i in max(1, N - (N % n))..<N {
a[i] = 1
}
这有什么特殊原因吗?我知道 Swift 数组在内存中可能不是连续的,但是从单个线程访问每个索引的内存位置一次,不应该做任何太有趣的事情。
最佳答案
数组不是线程安全的,尽管它们桥接到 Objective-C 对象,但它们的行为就像具有 COW(写入时复制)逻辑的值类型。当任何元素发生变化且引用计数器大于 1 时,数组上的 COW 将复制整个数组(从概念上讲,实际实现要更微妙一些)。
只要主线程碰巧引用和元素,对数组进行更改的线程就会触发内存复制。主线程也会进行更改,因此也会导致 COW。最终得到的是任一线程使用的最后修改副本的状态。这将随机地留下一些不确定的变化并解释“错过”的项目。
为了避免这种情况,您需要在特定线程中执行所有更改并使用sync()来确保数组上的COW仅由该线程执行(这实际上可能会减少内存副本的数量并为非常大的数组)。不过,使用这种方法会产生开销和潜在的争用。这是为了线程安全而付出的代价。
解决此问题的另一种方法是使用对象数组(引用类型)。这使您的数组成为一个简单的指针列表,这些指针实际上不会通过修改引用对象中的数据而更改。尽管在实际程序中,您需要注意每个对象实例内的线程安全性,但与值类型数组相比,干扰(和开销)要少得多。
关于arrays - Swift 并行性 : Array vs UnsafeMutablePointer in GCD,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46011056/