Swift 并行性:GCD 中的数组与 UnsafeMutablePointer

用户8388622

我发现了一些奇怪的东西:无论出于何种原因,在以下代码运行后,它的数组版本几乎总是包含随机 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 数组在内存中可能不是连续的,但是从单个线程访问每个索引的内存位置应该不会太有趣。

阿兰·T。

数组不是线程安全的,尽管它们被桥接到 Objective-C 对象,但它们的行为与 COW(写时复制)逻辑的值类型相同。当任何元素发生变化并且引用计数器大于 1 时,数组上的 COW 将复制整个数组(从概念上讲,实际实现有点微妙)。

每当主线程碰巧引用和元素时,对数组进行更改的线程将触发内存复制。主线程也会进行更改,因此也会导致 COW。您最终得到的是任一线程使用的最后修改副本的状态。这将随机留下一些不确定的变化并解释“错过”的项目。

为避免这种情况,您需要在特定线程中执行所有更改并使用 sync() 以确保阵列上的 COW 仅由该线程执行(这实际上可能会减少内存副本的数量并为非常大的阵列提供更好的性能)。但是,使用这种方法会产生开销和潜在的争用。这是为线程安全付出的代价。

解决此问题的另一种方法是使用对象数组(引用类型)。这使您的数组成为一个简单的指针列表,这些指针实际上并未通过修改引用对象中的数据而改变。尽管在实际程序中,您需要注意每个对象实例中的线程安全性,但干扰(和开销)要比使用值类型数组少得多。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章