2011-07-04 2 views
2

항목을 업데이트하거나 2 개의 다른 스레드에서 새로운 데이터를 삽입하거나 제거 할 수있는 목록이 있습니다.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 영업 이익 항목이 목록에 업데이트 할 수 있음을 주장하기 때문이다. 이것은 ConcurrentQueue 을 사용하여 영향을줍니다. –

+0

@ 리차드 : 알고리즘이 수행하는 작업에 따라 다릅니다. 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 
관련 문제