具有多线程的LINQ查询

时间:2014-12-21 16:02:56

标签: c# vb.net multithreading linq

我有一个多线程应用程序,其中多个线程操纵列表(Of T),添加,删除和查询列表。其中一个线程查询列表,然后需要迭代结果集。当然,预计会得到异常表示在迭代过程中集合发生了变化,由于某种原因与查询的性质和迭代行为有关,我无法在此处锁定列表,所以我试图得到使用.ToList()函数的结果副本,但奇怪的是我仍然收到相同的错误。我以为.ToList()函数获取结果的分隔列表,但它看起来并不像。有没有其他方法可以将查询结果放入新列表中?

1 个答案:

答案 0 :(得分:1)

如果没有同步,您无法从多个线程访问List<T>的实例,因为结果行为未定义,并且在大多数情况下会导致问题。你已经找到了一个。

创建某种ConcurrentCollection并同步所需的所有操作:

public class ConcurrentCollection<T> : IEnumerable<T>
{
    private readonly List<T> innerList = new List<T>();

    public void Add(T item)
    {
        lock (innerList)
        {
            innerList.Add(item);
        }
    }

    public bool TryRemove(T item)
    {
        lock (innerList)
        {
            return innerList.Remove(item);
        }
    }

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

    public IEnumerator<T> GetEnumerator()
    {
        lock (innerList)
        {
            return (IEnumerator<T>) this.innerList.ToArray().GetEnumerator();
        }
    }

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

这里的关键点是返回的枚举器枚举列表的快照(ToArray),因此如果在两者之间修改它并不重要。

请记住,你必须创建&#34; atomic&#34;您的呼叫代码的操作。让我们假设您只想在列表为空的情况下从一个线程添加项目。这是错误的:

if (collection.Count == 0)
{
    collection.Add(item);
}

而是将另一个成员添加到ConcurrentCollection ...

public bool AddIfEmpty(T item)
{
    lock (innerList)
    {
        if (innerList.Count == 0)
        {
            innerList.Add(item);
            return true;
        }

        return false;
    }
}

...并像这样使用它:

var itemWasAdded = collection.AddIfEmpty(item);