Swift Parallelism:GCD中的Array与UnsafeMutablePointer

时间:2017-09-02 06:19:08

标签: arrays swift pointers parallel-processing

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

1 个答案:

答案 0 :(得分:0)

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

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

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

另一种方法是使用对象数组(引用类型)。这使得您的数组成为一个简单的指针列表,这些指针实际上并未通过修改引用对象中的数据而更改。虽然在实际程序中,您需要考虑每个对象实例中的线程安全性,但是与值类型数组相比,干扰(和开销)要小得多。