列表的Threadsafe foreach枚举

时间:2020-03-05 18:54:26  来源:igfitidea点击:

我需要枚举对象的通用IList <>。列表的内容可能会更改,就像其他线程添加或者删除的一样,这将终止我的枚举,并带有"修改了集合;枚举操作可能不会执行"。

在IList <>上执行线程安全foreach的好方法是什么?大概不克隆整个列表。无法克隆列表引用的实际对象。

解决方案

回答

Forech取决于收集不会改变的事实。如果要遍历可以更改的集合,请使用法线构造,并为不确定的行为做好准备。锁定可能是一个更好的主意,具体取决于我们正在执行的操作。

回答

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

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

回答

克隆列表是最简单,最好的方法,因为它可以确保列表不会在下方更改。如果该列表太大而无法克隆,请考虑在该列表周围放置一个锁,在对其进行读/写操作之前必须先对其进行锁定。

回答

将列表包装在锁定对象中以进行读取和写入。如果我们拥有合适的锁,甚至可以同时遍历多个读取器,这样既可以允许多个并发读取器,也可以允许一个写入器(没有读取器时)。

回答

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

我想到了几种可能性:

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

另外,我们可以编写自己的IList和IEnumerator实现,以实现所需的并行访问。但是,恐怕这不会很简单。

回答

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

从MSDN

回答

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

最好的方法依赖于ReadWriteResourceLock,由于所谓的Convoy问题,ReadWriteResourceLock常常会导致性能问题。

我发现有关该主题的最佳文章是Jeffrey Richter撰写的这篇文章,它公开了自己的高性能解决方案方法。

回答

因此要求是:​​在同时添加和删除元素的同时,我们需要通过IList <>进行枚举而不创建副本。

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

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

回答

简单索引数据结构(例如链表,b树或者哈希表)的默认行为是按从第一个到最后的顺序枚举。在迭代器已经超过该点之后在数据结构中插入一个元素,或者在迭代器到达之后插入该迭代器将枚举的元素,这不会引起问题,如果事件可以被应用程序检测并处理,应用程序需要它。为了检测出集合中的变化并在枚举期间抛出错误,我只能想象是某人(不好)做他们想程序员想要的事情的想法。确实,Microsoft已修复了其收藏集以使其正常工作。他们在.NET 4.0中将其闪亮的全新不间断集合称为ConcurrentCollections(System.Collections.Concurrent)。