【问题标题】:Atomic List - is this collection threadsafe and fast?原子列表 - 这个集合线程安全且快速吗?
【发布时间】:2012-06-18 14:55:05
【问题描述】:

我正在尝试找出最好的方法来拥有一个集合,我将能够同时从不同的线程中迭代、添加项目和删除所有项目。性能对于迭代组件至关重要,但不会经常添加和删除项目 - 因此添加/删除性能稍微不那么重要。

这是我想出的:

public class AtomicList<T> : IEnumerable<T>
{
    private readonly object Locker = new object();
    private List<T> InternalCollection = new List<T>();

    public void Add(T Value)
    {
        lock (Locker)
        {
            List<T> Update = new List<T>(InternalCollection);
            Update.Add(Value);
            InternalCollection = Update;
        }
    }

    public void Remove(T Value)
    {
        lock (Locker)
        {
            List<T> Update = new List<T>(InternalCollection);
            Update.Remove(Value);
            InternalCollection = Update;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return InternalCollection.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

这样做有什么缺点吗?会不会像我上面描述的那样是线程安全/可迭代的?

另外 - 使用 foreach 和 IEnumerable 是否有任何性能成本?如果我直接访问 InternalCollection 并对其进行 for 语句会更好吗?

【问题讨论】:

  • 为什么不使用像ConcurrentBag这样的现有并发列表?
  • 但是你能遍历并发包并同时添加/删除吗?
  • @L.B - 只是出于好奇 - 既然你们拥有 CodeReview 网站,StackOverflow 的权限是什么?
  • @William - 代码审查是为了获取一段工作代码并分析它以获得最佳实践。 Stack Overflow 用于解决编程相关的问题。

标签: c# multithreading collections


【解决方案1】:

如果您不同时调用 Add 和 Remove 方法,则不需要 locks,因为您只是从 InternalCollection 字段引用的列表中读取。使用Interlocked.Exchange Method 以线程安全的方式将旧列表替换为新列表:

public class AtomicList<T> : IEnumerable<T>
{
    private List<T> internalCollection = new List<T>();

    public void Add(T vlue)
    {
        List<T> update = new List<T>(internalCollection);
        update.Add(value);
        Interlocked.Exchange(ref internalCollection, update);
    }

    public bool Remove(T value)
    {
        List<T> update = new List<T>(internalCollection);
        bool removed = update.Remove(value);
        if (removed) Interlocked.Exchange(ref internalCollection, update);
        return removed;
    }

    public IEnumerator<T> GetEnumerator()
    {
        ...

【讨论】:

  • 这不会阻止在枚举期间修改支持List&lt;T&gt;List&lt;T&gt; 在这方面不是线程安全的。
  • 后备列表未修改。
  • 刚刚注意到...任何以前获取的枚举器仍然引用旧的支持实例。
  • 这有点复制繁重,但它应该适用于小型列表或很少修改的列表。
  • 另一个问题是当 2 个线程尝试同时修改集合时...有可能一个修改不会反映在最终集合中(线程 2 将覆盖线程一的修改)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-30
  • 2011-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多