2009-09-22 152 views
42

特別C#數組是否線程安全?

  1. 創建函數採取陣列和索引作爲參數。
  2. 創建一個n元素數組。
  3. 創建一個n計數循環。
  4. 上一個新的線程在循環中分配對象的新實例,使用傳入的索引數組。

我知道如何管理線程等。我很感興趣,知道這是線程安全的做事方式。

class Program 
{ 
    // bogus object 
    class SomeObject 
    { 
     private int value1; 
     private int value2; 

     public SomeObject(int value1, int value2) 
     { 
      this.value1 = value1; 
      this.value2 = value2; 
     } 
    } 

    static void Main(string[] args) 
    { 

     var s = new SomeObject[10]; 
     var threads = Environment.ProcessorCount - 1; 
     var stp = new SmartThreadPool(1000, threads, threads); 
     for (var i = 0; i < 10; i++) 
     { 
      stp.QueueWorkItem(CreateElement, s, i); 
     } 

    } 

    static void CreateElement(SomeObject[] s, int index) 
    { 
     s[index] = new SomeObject(index, 2); 
    } 
} 
+20

這是完全沒有問題的觀點,但我會建議不要使用'Environment.ProcessorCount - 1' - 那些可憐的單核人「會是徹頭徹尾的不快樂,否則...... – 2009-09-24 17:29:33

+6

還有人用單核心處理器? – Gary 2009-09-24 18:15:31

+4

@Gary有虛擬機,帶有「一個」處理器; – IamIC 2011-12-29 10:18:26

回答

39

我相信如果每個線程只能在數組的單獨部分工作,一切都會好的。如果你要共享數據(即在線程之間傳遞它),那麼你需要某種內存屏障來避免內存模型問題。

相信,如果您產卵一堆線程,每個填充自己的數組的部分,然後等待所有這些線程的使用Thread.Join來完成,即會做足夠的用於柵欄條款你是安全的。我目前沒有任何支持文件,請注意你...

編輯:你的示例代碼是安全的。任何時候都有兩個線程訪問同一個元素 - 就好像它們每個都有單獨的變量。但是,這並不總是有用的。在某些情況下,線程通常會希望共享狀態 - 一個線程會想要讀取另一個線程寫入的內容。否則,他們寫入共享數組而不是寫入他們自己的私有變量是沒有意義的。 這就是你需要小心的地方 - 線程之間的協調。

+3

數組元素遵循與常規變量相同的規則,因爲交換引用將是原子的,但替換結構通常不會。儘管如此,這並沒有考慮到對內存屏障的需求,使訪問變得不穩定。正如你所說的,是否安全取決於編碼器正在做什麼。 – 2009-09-22 15:18:02

+0

@Steven:是的,那是關於我的理解:) – 2009-09-22 15:23:42

+2

@Steven「是否安全將取決於編碼器正在做什麼。」 - 和說「不,這不是安全的。」 ? – Rik 2009-09-22 15:25:58

24

MSDN documentation on Arrays說:

公共靜態(在Visual Basic中的Shared)這種類型的 成員都是線程安全的。 任何實例成員不是 保證是線程安全的。

此實現不提供 同步(線程安全)包裝 數組;但是,基於Array的.NET Framework 類使用SyncRoot 屬性提供了 自己的同步版本 集合。

枚舉整個集合是 本質上不是線程安全的 過程。即使集合是 同步,其他線程仍然可以 修改集合,這將導致枚舉器引發 引發異常。 要在枚舉中保證線程安全,您可以在整個 枚舉期間鎖定 集合,或者捕獲由其他 線程所做更改產生的異常 。

所以不,他們不是線程安全的。

+13

這不能回答我的問題。我沒有進行任何列舉,這傢伙怎麼能得到13票? – Gary 2009-09-22 16:16:28

+4

投票表示答案有幫助。您的選中標記表示答案可解決您的問題。考慮到這些信息,如果我正在開發一個帶數組的線程化類,我相信我會做的第一件事就是觸發Reflector,打開一個在線程安全的上下文中使用Array的Framework類,並查看它們在做什麼與SyncRoot。 – 2009-09-22 18:59:20

+6

這個答案確實沒有幫助,因爲MSDN的報價很具誤導性*和*不重要。對引用類型的數組訪問是原子的;甚至列舉出這樣一個數組(儘管可能是不明智的)就是在線程安全的情況下。 – 2009-09-24 17:25:58

11

通常,當一個集合被認爲是「不是線程安全的」,這意味着併發訪問可能會在內部失敗(例如,讀取列表< T>的第一個元素並不安全,而另一個線程在列表:列表< T>可能調整底層數組的大小,並且在將數據複製到數組之前,讀訪問可能會轉到新數組)。

這樣的錯誤對於數組是不可能的,因爲數組是固定大小的並且沒有這樣的「結構變化」。 具有三個元素的數組與三個變量相比不會更多或更少線程安全。

C#規範對此沒有任何說明;但很明顯,如果您知道IL並閱讀CLI規範 - 您可以爲數組內的元素獲得託管引用(如用於C#「ref」參數的引用),然後執行正常和不穩定負載並存儲到該元素。 CLI規範描述了這種加載和存儲的線程安全保證(例如,元素的原子性< = 32位)

所以,如果我正確地解決了你的問題,你想用不同的線程填充一個數組,分配給每個數組元素只有一次?如果是這樣,那完全是線程安全的。

4

您提供的示例與Microsoft自己的C#4.0並行擴展的工作方式非常相似。

for循環:

for (int i = 0; i < 100; i++) { 
    a[i] = a[i]*a[i]; 
} 

成爲

Parallel.For(0, 100, delegate(int i) { 
    a[i] = a[i]*a[i]; 
}); 

所以,是的,你的榜樣應該沒問題。關於C#中新的並行支持,以下是舊版本blog post