Threadsafe foreach枚举列表

我需要枚举通用的IList 对象。 列表的内容可能会更改,如在其他线程中添加或删除,这将使用“已修改集合;枚举操作可能无法执行”来终止我的枚举。

在IList 上执行线程安全foreach的好方法是什么? 可以预先没有克隆整个列表。 无法克隆列表引用的实际对象。

克隆列表是最简单和最好的方法,因为它可以确保您的列表不会从您的下方更改。 如果列表太大而无法克隆,请考虑在读取/写入之前必须对其进行锁定。

您的问题是枚举不允许IList更改。 这意味着您必须在浏览列表时避免这种情况。

我想到了一些可能性:

  • 克隆列表。 现在每个枚举器都有自己的副本来处理。
  • 序列化对列表的访问。 使用锁定以确保在枚举时没有其他线程可以修改它。

或者,您可以编写自己的IList和IEnumerator实现,它允许您需要的那种并行访问。 但是,我担心这不会很简单。

你会发现这是一个非常有趣的话题。

最好的方法依赖于ReadWriteResourceLock,由于所谓的Convoy问题,它会产生很大的性能问题。

我发现这篇文章的最佳文章是杰弗里里希特(Jeffrey Richter)提出的最佳文章,它揭示了自己的高性能解决方案。

没有这样的操作。 你能做的最好的是

lock(collection){ foreach (object o in collection){ ... } } 

Forech取决于收集不会改变的事实。 如果要迭代可以更改的集合,请使用构造的法线并准备好非确定性行为。 锁定可能是一个更好的主意,取决于你正在做什么。

 ICollection MyCollection; // Instantiate and populate the collection lock(MyCollection.SyncRoot) { // Some operation on the collection, which is now thread safe. } 

来自MSDN

所以要求是:你需要通过IList <>枚举,而不需要复制同时添加和删除元素。

你能澄清几件事吗? 插入和删除是仅在列表的开头或结尾发生的吗? 如果可以在列表中的任何位置进行修改,那么在枚举当前元素的位置附近或位置附近移除或添加元素时,枚举应该如何表现?

这当然可以通过创建一个带有整数索引的自定义IEnumerable对象来实现,但前提是您可以控制对IList <>对象的所有访问(用于锁定和维护枚举的状态)。 但在最好的情况下,多线程编程是一项棘手的业务,这是一个复杂的概率。

简单索引数据结构(如链表,b树或散列表)的默认行为是从第一个到最后一个按顺序枚举。 在迭代器已经超过该点之后在数据结构中插入一个元素或者插入一个迭代器一旦到达就会枚举一个元素就不会产生问题,并且应用程序可以检测到这样的事件并处理申请需要它。 为了检测集合中的变化并在枚举期间抛出错误,我只能想象有人(不好)想要做他们认为程序员想要的东西。 实际上,Microsoft已经修复了他们的集合以正常工作。 他们在.NET 4.0中调用了他们shiny的新的不间断集合ConcurrentCollections(System.Collections.Concurrent)。

将列表包装在锁定对象中以进行读写。 如果你有一个合适的锁,你甚至可以同时迭代多个读者,这允许多个并发读者,但也允许一个单独的读者(当没有读者时)。