使用2个不同线程的列表?

时间:2011-07-04 22:29:40

标签: c# multithreading list locking

我有一个列表,可以更新条目,从2个不同的线程中插入或删除新数据。

是否可以使用公共只读对象锁定何时用于与其他线程进行交互,以确定何时被锁定或者在2个线程中使用此列表的正确方法是什么? / p>

3 个答案:

答案 0 :(得分:5)

访问不同线程上的列表时,应始终使用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.
  }

答案 1 :(得分:4)

您应该将此包装在一个处理锁定的类中,或者使用线程安全的集合,例如ConcurrentQueue<T>System.Collections.Concurrent中的其他集合。

将同步对象公开给公共API是危险的,这不是一个好习惯。

答案 2 :(得分: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示例可能仍会崩溃,但它允许迭代,跨线程添加和删除。如果要公开互斥锁,请执行此操作,然后为其他代码构造(如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