2010-10-28 33 views
15

我可以不鎖定,從多個線程安全地調用List.AddRange(r)?如果不是,我會遇到什麼樣的麻煩?是列表<T> .AddRange()線程安全嗎?

+1

btw - 「有什麼樣的麻煩?」 - 你可能會得到一個異常,在一些隨機的時間多線程衝突 – 2010-10-28 20:34:31

回答

17

沒有,its documentation不說它是線程安全的,因此它不是。

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

至於什麼可以去錯了,想什麼的AddRange(newItems)作用:

  • 檢查是否存在內部數組中足夠的空間
  • 如果不是:
    • 分配新陣列
    • 將當前項目複製到新陣列
    • 將字段設置爲指向新的陣列唉
  • 複製newItems正確的本地內部數組
  • 更新在「計數」字段(這是用來控制下一個項目插入)

現在想想會出現什麼如果上述情況與對AddRange()的另一個調用混合在一起,或者甚至只是一個讀取某個項目的調用,都會發生。

1

不,它不是線程安全的。

線程A可以打電話給你的AddRange名單上。它可以在收集和切換線程上部分迭代。

線程B可以調用添加/刪除等前線程A已經完成。

6

不,但我想補充說,在一個鎖內執行myList.AddRange(...);要比做幾個lock (syncLock) { myList.Add(...) };更有效率。

哪種類型的麻煩,你會碰上?當一個線程正在添加一個項目,而另一個線程正在枚舉列表時,List<T>會拋出一個異常,因爲它會執行一些內部版本控制,因爲它想阻止我們可憐的開發人員碰到令人討厭的副作用。

另外,List<T>在內部保存在其中存儲其項的數組。也許在一個數組中設置一個項目是非常原子的,但是每當這個數組的容量達到時,就會創建一個新的項目,並且項目將從舊項目中複製過來。因此,當一個線程想要在發生複製時添加某些內容時,可以想象事情會不同步。

+0

優秀的答案,謝謝。 =) – 2010-10-28 14:40:27

3

在.NET Framework 4.0之前,沒有.NET集合是線程安全的。然後您將被要求鎖定它,然後在代碼 Collections and Synchronization (Thread Safety)中訪問它。

在另一方面,.NET Framework 4中。0引入包含細粒度Thread-Safe Collections的新名稱空間System.Collections.Concurrent

最後,如果你可以使用.NET Framework 4.0中,我強烈建議你這樣做對你所需要的,否則,請務必要修改或訪問每次鎖定集合。

此外,靜態集合應該是線程安全的,但要注意,作爲成員,但不保證。

編輯#1

後由於史蒂夫湯森的評論進一步驗證,我承認有.NET框架中的三個線程安全的集合開始3.0版本:

  1. SynchronizedCollection Generic Class;
  2. SynchronizedKeyedCollection Generic Class;
  3. SynchronizedReadOnlyCollection Generic Class

我很抱歉,我剛剛得知自己exitense自己。 =)

+0

不正確的,也有一些在'System.Collections.Generic'在這裏工作,早於4.0 – 2010-10-28 18:14:48

+0

@Steve湯森:驗證後,你是對的,我錯了。此外,'IList '泛型集合不是,雖然我沒有考慮這個'SynchronizedCollection ',因爲我不知道它。 = P謝謝你通知我! – 2010-10-28 18:43:38

+1

沒問題。我欠你一個提及4.0集合的+1。 – 2010-10-28 18:48:42

3

根據您的使用,SynchronizedCollection可以工作。

你必須沒有單次AddRange雖然。如果您僅使用此操作爲種子收集種子,則可以這樣做,因爲存在IEnumerable構造函數重載。

+1

+1我剛剛學到了一些新東西!謝謝! =) – 2010-10-28 18:44:04

+0

感謝您的upvote。我想告訴你,我編輯了我的答案,以反映你向我提供的這些新信息(我們所有人)。 – 2010-10-28 18:55:02