2009-07-02 68 views
8

我有一個C#中的情況,我有一個簡單類型的列表。該列表可以被多個線程訪問:可以添加或刪除條目,並且可以檢查條目的存在。我已經把這個列表封裝在一個對象中,這個對象只暴露了這三個操作。C#併發列表問題

我有幾個案例需要處理(與我剛剛提到的方法不完全相同)。
1.線程可以檢查是否存在條目。 (簡單)
2.線程可以檢查條目是否存在,如果條目不存在,則添加它。
3.線程需要檢查一個條目是否存在,如果存在,請等到刪除。
4. 2和3的組合,其中一個線程檢查條目是否存在,如果它存在,它必須等到它被刪除後才能添加它自己。

整個想法是,一個條目的存在意味着一個鎖。如果存在條目,則其標識的對象不能更改,並且代碼無法繼續進行,因爲它正在其他地方進行修改。

這些看起來像是簡單的新手,但我在併發問題上提醒自己,這讓我有點偏執,而且我也不熟悉C#的併發機制。

什麼是最好的處理方法?我完全沒有?應該檢查並添加(測試和設置?)合併成第四個原子操作?我是否只是將鎖塊添加到列表訪問的方法中?

另外,是否有可能單元測試這種事情(不是簡單的操作,併發情況)?

回答

8

單元測試肯定會很難。

這可以通過.NET中的「本機」併發機制合理完成:鎖定語句和Monitor.Wait/Monitor.PulseAll。除非每個項目都有單獨的監視器,否則只要刪除了任何內容,就需要喚醒所有線程 - 否則,您將無法告訴「正確」線程被喚醒。

如果真的只是一個設置項目,你可能想使用的HashSet<T>代替List<T>代表收集,順便說一句 - 你提到什麼是排序的事情。

示例代碼,假設一組是好的爲您提供:

using System; 
using System.Collections.Generic; 
using System.Threading; 

public class LockCollection<T> 
{ 
    private readonly HashSet<T> items = new HashSet<T>(); 
    private readonly object padlock = new object(); 

    public bool Contains(T item) 
    { 
     lock (padlock) 
     { 
      return items.Contains(item); 
     } 
    } 

    public bool Add(T item) 
    { 
     lock (padlock) 
     { 
      // HashSet<T>.Add does what you want already :) 
      // Note that it will return true if the item 
      // *was* added (i.e. !Contains(item)) 
      return items.Add(item); 
     } 
    } 

    public void WaitForNonExistence(T item) 
    { 
     lock (padlock) 
     { 
      while (items.Contains(item)) 
      { 
       Monitor.Wait(padlock); 
      } 
     } 
    } 

    public void WaitForAndAdd(T item) 
    { 
     lock (padlock) 
     { 
      WaitForNonExistence(item); 
      items.Add(item); 
     } 
    } 

    public void Remove(T item) 
    { 
     lock (padlock) 
     { 
      if (items.Remove(item)) 
      { 
       Monitor.PulseAll(padlock); 
      } 
     } 
    } 
} 

(沒有經過充分測試,誠然你可能還需要爲等待代碼指定超時...)

+0

+1,我來到了一個類似的解決方案,但HashSet中,使之更有效率。 Monitor類的很好的演示,使用嵌套鎖等。只有當一個項目被刪除時,問題可能是'踩踏'。 – 2009-07-02 17:23:59

8

雖然# 1可能是最簡單的寫法,它本質上是一種無用的方法。除非在完成對「存在條目」的查詢之後持有相同的鎖,否則實際上您將返回「過去某個時刻存在條目」。它沒有給你任何有關目前存在的信息。

在發現列表中的某個值然後執行任何操作以檢索值,刪除該值之後,可能會出現另一個線程並將其移除。

包含對併發列表的操作應該與您計劃在該檢查是否存在的情況下執行的操作相結合。例如TestAdd()或TestRemove()是安全比包含+添加或包含+刪除

+0

您應該鏈接到您的博客文章的線程安全收集界面。 – 2009-07-02 17:10:53

+0

是的,但是#4就是這樣。其他方法的可用性值得懷疑,但不一定是錯誤的。 – 2009-07-02 17:12:33

1

有一種產品用於查找單元測試中的競爭條件等。它被稱爲TypeMock Racer。不過,我不能說任何支持或反對它的效果。 :)