2011-07-04 39 views
2

我有一個列表,它可以更新條目,插入或從兩個不同的線程中刪除新數據。使用來自2個不同線程的列表?

使用公共只讀對象鎖定它是否被用於與其他線程交互時,它是否被鎖定,或者在兩個線程中使用此列表的正確方法是什麼?

回答

4

訪問不同線程上的列表時,應始終使用lock

public class Sample 
{ 
    object synch = new object(); 
    List<Something> list = new List<Something>(); 

    void Add(Something something) 
    { 
     lock (synch) { list.Add(something); } 
    } 

    // Add the methods for update and delete. 
    } 
4

你應該在處理鎖定爲你包裝類本,或使用一個線程安全的集合,如ConcurrentQueue<T>System.Collections.Concurrent其他的收藏品之一。

將同步對象暴露給公共API是危險的,這不是一個好習慣。

+0

-1,因爲OP指出,項目可以在列表中進行更新。這隱含了使用ConcurrentQueue 。 –

+0

@Richard:取決於算法的作用 - remove + add可能是完全可以接受的。在線程場景中被稱爲「替換」的項目有其自身的一些困難...... –

2

首先,閱讀這篇文章,瞭解爲什麼它是壞的:反正http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

然後,做到像我一樣:

public abstract class ConcurrentCollection<T> : ICollection<T> 
{ 
    private List<T> List { get; set; } 

    public ConcurrentCollection() 
    { 
     this.List = new List<T>(); 
    } 

    public T this[int index] 
    { 
     get 
     { 
      return this.List[index]; 
     } 
    } 

    protected virtual void AddUnsafe(T item) 
    { 
     this.List.Add(item); 
    } 

    protected virtual void RemoveUnsafe(T item) 
    { 
     this.List.Remove(item); 
    } 

    protected virtual void ClearUnsafe() 
    { 
     this.List.Clear(); 
    } 

    public void Add(T item) 
    { 
     lock (this.List) 
     { 
      this.AddUnsafe(item); 
     } 
    } 

    public bool Remove(T item) 
    { 
     lock (this.List) 
     { 
      this.RemoveUnsafe(item); 
      return true; 
     } 
    } 

    public void Clear() 
    { 
     lock (this.List) 
     { 
      this.ClearUnsafe(); 
     } 
    } 

    public int Count 
    { 
     get 
     { 
      lock (this.List) 
      { 
       return this.List.Count; 
      } 
     } 
    } 

    public bool IsReadOnly 
    { 
     get 
     { 
      return false; 
     } 
    } 

    public bool Contains(T item) 
    { 
     lock (this.List) 
     { 
      return this.List.Contains(item); 
     } 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     lock (this.List) 
     { 
      this.List.CopyTo(array, arrayIndex); 
     } 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return new ConcurrentEnumerator<T>(this.List, this.List); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException("Abstract concurrent enumerators not implemented."); 
    } 
} 

public class ConcurrentEnumerator<T> : IEnumerator<T> 
{ 
    private int Position = -1; 
    private List<T> Duplicate; 
    private object Mutex; 
    private ICollection<T> NonConcurrentCollection; 

    internal ConcurrentEnumerator(ICollection<T> nonConcurrentCollection, object mutex) 
    { 
     this.NonConcurrentCollection = nonConcurrentCollection; 
     this.Mutex = mutex; 

     lock (this.Mutex) 
     { 
      this.Duplicate = new List<T>(this.NonConcurrentCollection); 
     } 
    } 

    public T Current 
    { 
     get 
     { 
      return this.Duplicate[this.Position]; 
     } 
    } 

    object IEnumerator.Current 
    { 
     get 
     { 
      return this.Current; 
     } 
    } 

    public bool MoveNext() 
    { 
     this.Position++; 

     lock (this.Mutex) 
     { 
      while (this.Position < this.Duplicate.Count && !this.NonConcurrentCollection.Contains(this.Current)) 
      { 
       this.Position++; 
      } 
     } 

     return this.Position < this.Duplicate.Count; 
    } 

    public void Reset() 
    { 
     this.Position = -1; 
    } 

    public void Dispose() { } 
} 

// Standards have List as derived Collection... 
public class ConcurrentList<T> : ConcurrentCollection<T> { } 

該代碼還沒有完全安全的,比如伯爵例如可仍然崩潰,但它允許迭代,跨線程添加和刪除。如果你想公開這個互斥鎖,那就這樣做,然後鎖定它,以獲得像count和contains之類的其他代碼結構。

但它仍然是一個壞主意。

編輯:示例用法。

ConcurrentList<string> list = new ConcurrentList<string>(); 

list.Add("hello"); 
list.Add("world"); 
list.Add("foo"); 
list.Add("bar"); 

foreach (string word in list) 
{ 
    if (word == "world") 
    { 
     list.Remove("bar"); // Will not crash the foreach! 
    } 

    Console.WriteLine(word); 
} 

輸出:

hello 
world 
foo 
相關問題