IEnumerable <T>线程安全吗?

e36*_*6M3 12 .net c# asp.net multithreading

我有一个填充a的主线程List<T>.此外,我创建了一个将在不同线程上执行的对象链,需要访问List.原始列表在生成后永远不会被写入.我的想法是将列表传递IEnumerable<T>给在其他线程上执行的对象,主要是因为不允许那些实现这些对象的人错误地写入列表.换句话说,如果保证不写入原始列表,多个线程使用.WhereforeachIEnumerable是否安全?

如果原始集合永远不会改变,我不确定迭代器本身是否是线程安全的.

And*_*rey 12

IEnumerable<T>无法修改.那么什么可以是非线程安全的呢?(如果你不修改实际List<T>).

对于非线程安全,您需要编写和读取操作.

"迭代器本身"是为每个实例化的foreach.

编辑:我简化了我的回答,但@Eric Lippert补充了有价值的评论.IEnumerable<T>没有定义修改方法,但它并不意味着接入运营商是线程安全的(GetEnumerator,MoveNext等),最简单的例子:GetEnumerator因为这实现的:

  • 每次都返回相同的实例 IEnumerator
  • 重置它的位置

更复杂的例子是缓存.

这是有趣的一点,但幸运的是我不知道任何没有线程安全实现的标准类IEnumerable.

  • 仅仅因为调用者*无法修改IE <T>*并不能保证其*内部工作*对多个读者来说是线程安全的!IE <T>的实现可能包含缓存或其他性能增强算法,这些算法涉及写入共享数据结构,而那些可能不是多个读取器的线程安全.仅仅因为该集合在逻辑上*没有变异就不需要在枚举期间***被突变. (21认同)
  • @Andrey:我不知道,但这并不意味着没有.例如,考虑一个使用哈希表实现的字典,正如您所期望的那样,但有一个转折点:当您访问位于存储桶列表末尾的密钥时,它会将该存储桶移动到存储桶的前端名单.这样的字典在编译器中很常见,因为编译器经常会出现这样的情况:相同的标识符将在代码中多次出现.在读取时,可以动态地重写表内部,以尝试使将来的读取更快. (4认同)

Phi*_*hil 8

调用Where或foreach的每个线程都有自己的枚举器 - 它们不共享同一列表的一个枚举器对象.因此,由于未对List进行修改,并且由于每个线程都使用自己的枚举器副本,因此不应存在线程安全问题.

您可以在一个线程中看到这个 - 只需创建一个包含10个对象的List,并从该List中获取两个枚举器.使用一个枚举器枚举5个项目,并使用另一个枚举5个项目.您将看到两个枚举器仅通过前5个枚举枚举,而第二个枚举器没有从第一个枚举器停止的位置开始.


Bri*_*eon 6

只要您确定List不会修改,就可以安全地从多个线程读取。这包括使用IEnumerator它提供的实例。

对于大多数收藏来说都是如此。实际上,BCL中的所有集合在枚举过程中都应保持稳定。换句话说,枚举器将不会修改数据结构。我可以想到一些模糊的情况,例如splay树,在枚举它可能会修改结构。同样,没有BCL集合会这样做。