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