【问题标题】:InvalidOperationException: Collection Was ModifiedInvalidOperationException:集合已修改
【发布时间】:2012-03-12 06:17:48
【问题描述】:

我知道在迭代集合时我不能修改它。

通常我会复制我想要迭代的集合,然后迭代副本以避免错误。

我的程序;但是,对我要复制的集合进行了大量快速修改。

是否会因为我在修改父集合时制作副本而发生错误?

我知道 ConcurrentDictionary 为跨线程提供了某种锁定机制。我可以使用类似的方法来防止此错误吗?

    // Original collection:
    Dictionary<string, Enemy> Dict_Enemies = new Dictionary<string, Enemy>();

    // Copy of original collection: (this line throws the exception)
    Dictionary<string, Enemy> Dict_Enemies_Copy = new Dictionary<string, Enemy>(Dict_Enemies);

【问题讨论】:

  • 为什么不使用ConcurrentDictionary 代替Dict_Enemies
  • 当我使用 ConcurrentDictionary 时,我的程序似乎只是挂在通常会引发异常的地方。也许我在其他地方有问题。
  • 您可以使用ConcurrentDictionary.ToArray() 方法以线程安全的方式对其进行复制。看看我的回答。

标签: c# exception collections dictionary


【解决方案1】:

尝试为Dict_Enemies 使用ConcurrentDictionary 以允许从多个线程对其进行修改。然后使用ToArray() 方法在线程安全操作中复制它:

// Original collection:
var Dict_Enemies = new ConcurrentDictionary<string, Enemy>();

// Thread-safe copy:
ICollection<KeyValuePair<string, Enemy>> Dict_Enemies_Copy = Dict_Enemies.ToArray();

【讨论】:

  • 请注意,ConcurrentDictionary 的处理本身并不重要——您必须更改一些代码,因为 CD 上的某些操作(例如添加)可能会因为并发问题而返回“false”——在这种情况你必须重试操作! CD 很好,但如果你还没有封装你的访问权限,那就不行了
  • @CarstenKönig 同意。切换到ConcurrentDictionary 肯定需要对添加/删除项目的方式进行一些更改。不过也有multiple ways to approach the problem,视需求而定。
  • 我认为这解决了我的问题,谢谢。当我从并发字典中删除一个键时,我可以为“out”参数使用一个虚拟对象吗?假富; Dict.TryRemove(key, out foo);
  • @JeffSchweigler ConcurrentDictionary.TryRemove 方法在操作成功时返回在out 参数中删除的对象,在操作失败时返回default(TValue)。您必须为其传递一个参数,因为编译器需要它,但如果您不需要它,则不必使用实际结果。
  • 您不必为了安全地迭代它而创建ConcurrentDictionary 的副本,因为它们已经可以安全地迭代了。如果不是,那么ToArray 方法也不起作用!
【解决方案2】:

Dictionary&lt;K,V&gt; 不支持多个并发写入者,这意味着您必须已经拥有一个锁。只需确保在持有该锁的同时创建副本即可。

【讨论】:

  • 我没有锁,因为我不确定如何正确实现。如果我使用'lock(dictionary) { //dosomething// }',它会不会导致我的程序的其他部分因为无法访问字典而停止正常工作?
  • 我需要某种方法来锁定字典,但让其他方法检查锁定,等待解锁,然后继续。
  • 我尝试在迭代时锁定副本,但仍然出现异常...
  • 如果您每次使用字典时都没有锁定它(例如lock (dictionary)),那么您的代码不正确。它可能会奏效,但这只是因为你很幸运!
【解决方案3】:

将此字典封装为类的私有成员,并使用锁(在两个地方都有相同的锁对象,可能是字典本身)以确保写入和复制不会同时发生。不允许在其他任何地方直接访问字典。

【讨论】:

    【解决方案4】:

    但是类似的代码对我来说是完美的.....你能对你的代码做更多解释吗???

      Dictionary<int, Test> dicOrg = new Dictionary<int, Test>();//Original
      dicOrg.Add(1, new Test() {id= 1,name="name",add="add"});
      Dictionary<int, Test> dicCopy = new Dictionary<int,Test>(dicOrg);//Copy of dicOrg
    

    【讨论】:

    • 我有多种方法可以同时删除项目并将项目添加到 dicOriginal。我有多种方法可以同时复制 dicOriginal。无论我尝试制作 dicOriginal 的副本,我都会在多个位置遇到异常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-11
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    相关资源
    最近更新 更多