2017-09-02 55 views
0

我發現了一些奇怪的東西:無論出於何種原因,在下面的代碼運行後,這個數組版本幾乎總是包含隨機0,而指針版本不會。Swift並行性:在GCD中的數組vs vs不安全的可變指針

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數組在內存中可能不是連續的,但是從一個線程訪問相對於每個索引的內存位置只需要一次,不應該做任何有趣的事情。

回答

0

數組不是線程安全的,雖然它們橋接到Objective-C對象,但它們的行爲與COW(寫入時複製)邏輯的值類型相同。當任何元素改變並且引用計數器大於1時(概念上,實際實現稍微微妙),數組上的COW將複製整個數組。

只要主線程碰巧引用和元素,那麼對數組進行更改的線程將觸發內存副本。主線程也會進行更改,因此它也會導致COW。最終結果是兩個線程使用的最後修改副本的狀態。這將隨機留下一些變化,並解釋「錯過」的項目。

爲了避免這種情況,您需要在特定線程中執行所有更改並使用sync()來確保數組中的COW只由該線程執行(這實際上可能會減少內存副本的數量並提供更好的性能對於非常大的數組)。雖然使用這種方法會有開銷和潛在的爭用。這是支付線程安全性的代價。

解決這個問題的另一種方法是使用一個對象數組(引用類型)。這使得你的數組成爲一個簡單的指針列表,通過修改被引用對象中的數據實際上不會改變它。儘管在實際的程序中,您需要介意每個對象實例中的線程安全性,但與使用值類型的數組相比,其干擾(和開銷)要少得多。

+0

對象引用數組仍然是一個值,不是嗎? – Raphael

+0

我也想過這個。但是零不應該是「隨機的」。那麼,OP沒有舉例......也許他們只是沒有發現這種模式。 – Raphael

+0

是的,一個對象數組包含「值」,但它們是「引用類型」值。它們就像是指向實際內容的指針。更改「指向」的值(被引用的值)不需要更新數組中的指針。對於值類型,COW也執行內部間接尋址,但是語言完全隱藏了內存操作。這可能會引起混淆,但最終會出現值類型的雙重間接和引用類型的三重間接。不安全的指針比較是單向的。 –