2012-11-25 50 views
1

我有System.Collections.Generic.SynchronizedCollection共享集合。我們的代碼使用.Net 4.0任務庫跨越線程並將同步的集合傳遞給線程。到目前爲止,線程並沒有向集合中添加或刪除項目。但是需要其中一個線程的新需求必須從集合中刪除項目,而另一個線程只讀取集合。在從集合中刪除項目之前是否需要添加鎖定?如果是這樣,讀者線程是否是線程安全的?或建議最好的方式來獲得線程安全?同步收集線程安全

+0

如果您使用的是SynchronizedCollection,它應該已經被提供在該添加/從集合中刪除操作的適當的鎖定。 – Joe

+0

這是否也支持在從另一個線程中刪除項目時枚舉另一個線程中的集合?我是否需要鎖定枚舉部分? – Amzath

+0

現有的「SynchronizedCollection」的全部重點是爲所有操作提供線程安全的集合(它通過鎖定所有操作)。如果您使用的是ICollection ,那麼您可能需要自行鎖定。請注意,如果您的目標是4.0框架或更高版本,則可能會看到使用System.Collections.Concurrent時的性能改進...... – Joe

回答

2

是,SynchronizedCollection會做鎖定你。

如果您有多個讀者和只有一個作家,你可能想看看使用ReaderWriterLock,而不是SynchronizedCollection。

另外,如果你是淨4+然後看看System.Collections.Concurrent。這些類比SynchronizedCollection有更好的性能。

+1

同步收集對於多個閱讀器和一個作者是否是線程安全的? – Amzath

+0

是的,但效率不高 –

+0

我認爲這取決於使用集合的情況。例如:如果您嘗試通過特定值(例如,一個屬性值大於20的項目),並更改該項目,您沒有原子操作,但需要鎖定 – Offler

3

不,它不完全是線程安全的。嘗試在一個簡單的控制檯的應用程序下面,看看它是如何與一個異常崩潰:

var collection = new SynchronizedCollection<int>(); 

var n = 0; 

Task.Run(
    () => 
     { 
      while (true) 
      { 
       collection.Add(n++); 
       Thread.Sleep(5); 
      } 
     }); 

Task.Run(
    () => 
     { 
      while (true) 
      { 
       Console.WriteLine("Elements in collection: " + collection.Count); 

       var x = 0; 
       if (collection.Count % 100 == 0) 
       { 
        foreach (var i in collection) 
        { 
         Console.WriteLine("They are: " + i); 
         x++; 
         if (x == 100) 
         { 
          break; 
         } 

        } 
       } 
      } 
     }); 

Console.ReadKey(); 

enter image description here

請注意,如果您有ConcurrentBag更換SynchronizedCollection,你會得到線程安全:

var collection = new ConcurrentBag<int>(); 

SynchronizedCollection在此應用程序中根本不是線程安全的。改用Concurrent Collections。

4

亞歷山大已經指出SynchronizedCollection對於這種情況不是線程安全的。 SynchronizedCollection實際上包裝了一個普通的通用列表,並且只是將每個調用委託給基礎列表,並且圍繞該調用發出鎖。這也在GetEnumerator中完成。因此,枚舉器的獲取是同步的,但不是實際的枚舉。

var collection = new SynchronizedCollection<string>(); 
collection.Add("Test1"); 
collection.Add("Test2"); 
collection.Add("Test3"); 
collection.Add("Test4"); 

var enumerator = collection.GetEnumerator(); 
enumerator.MoveNext(); 
collection.Add("Test5"); 
//The next call will throw a InvalidOperationException ("Collection was modified") 
enumerator.MoveNext(); 

當使用foreach時,將以這種方式調用枚舉器。因此,在通過此數組枚舉之前添加ToArray()將不起作用,因爲這將首先枚舉到此數組中。 當你在你的foreach中做什麼時,這個枚舉可能會更快,所以它可以減少獲得併發問題的可能性。正如理查德指出的那樣:爲了真正的線程安全,請去System.Collections.Concurrent類。

+0

然而,在調用'ToArray'之前,您可以使用集合的'SyncRoot'屬性。 –