【问题标题】:InvalidOperationException with foreach even though the collection is locked即使集合已锁定,foreach 也会出现 InvalidOperationException
【发布时间】:2019-12-31 02:44:54
【问题描述】:

最近,我一直在尝试使用多线程来制作我的一个旧项目,显然集合是其中的一部分。问题是一段代码不断随机崩溃(不经常但仍然足够明显):

Monde 是一个 SynchronizedCollection,我将其锁定以防止同时写入,但它仍在抱怨它。有什么我想念的吗?

编辑:我知道 SynchronizedCollection 类仅在创建枚举期间自动锁定,因此简单的 foreach 是不够的,这就是为什么我将整个循环包含在 @ 987654325@ 块。问题在于,当线程位于 lock 块内时,集合以某种方式被修改,这不应该发生。

此外,.NET 4 并发 API 下的所有类都不能满足我的所有需求,所以如果我不想让整个代码库变得比现在更复杂,我真的别无选择。

【问题讨论】:

  • 我非常清楚这一点(请参阅我对 Siavash 答案的评论),这就是为什么我将整个循环包含在 lock 块中。它应该有效地锁定集合,但它没有,这就是问题所在
  • Physique 是做什么的?
  • 我无法重现该问题。在我的实验中,锁定SyncRoot 成功地防止了SynchronizedCollection(程序集System.ServiceModel)的任何并发突变。
  • 你能分享一个minimal reproducible example吗?

标签: c# multithreading collections


【解决方案1】:

首先,如果您使用的是SynchronizedCollection,那么您不需要将它包装在lock 中,因为它在后台使用锁定来执行操作。第二个SynchronizedCollection 来自.Net 2.0 时代,如果您要移植旧代码,那么您最好使用System.Collections.Concurrent 命名空间中的集合。它们更新、更优化并且不依赖锁定和阻塞策略。

https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent?redirectedfrom=MSDN&view=netframework-4.8

【讨论】:

  • 错误,SynchronizedCollection 仅在创建枚举器期间锁定,而不是在枚举本身期间锁定(这是有道理的,CLR 枚举器模型不允许这样的事情)。我的问题是,即使有锁,集合也会被修改,这违反了并发合同
  • @zdimension 你能测试一下使用SynchronizedReadOnlyCollection 代替SynchronizedCollection 是否能解决问题?
  • 更大的问题是你在 Physique 方法中做什么?它是否会修改集合?