线程安全收集修改

最近我在使用通用字典时遇到以下异常

发生InvalidOperationException。 一个集合被修改了

我意识到这个错误主要是因为我使用的静态字典中存在线程安全问题。

一点背景:我目前有一个应用程序有3种不同的方法与这个问题有关。

  • 方法A使用foreach遍历字典并返回一个值。
  • 方法B将数据添加到字典中。
  • 方法C改变字典中键的值。
  • 有时在迭代字典时,数据也被添加,这是导致此问题的原因。 我一直在我的代码的foreach部分获取这个异常,我遍历字典的内容。 为了解决这个问题,我用ConcurrentDictionary替换了通用字典,这里是我所做的细节。

    目的:我的主要目标是彻底删除异常

    对于方法B(其中增加了一个新的密钥的字典)我取代.AddTryAdd

    对于方法C (它更新字典的值),我没有做任何改变。 代码的粗略草图如下所示:

      static public int ChangeContent(int para)
      {
          foreach (KeyValuePair<string, CustObject> pair in static_container)
          {
                 if (pair.Value.propA != para ) //Pending cancel 
                 {
                    pair.Value.data_id = prim_id;    //I am updating the content
                    return 0;
    
                 }
          }
         return -2;
      }
    

    对于方法A - 我只是迭代字典,这是运行代码停止的地方(在调试模式下),Visual Studio告诉我这是错误发生的地方。我使用的代码与以下代码类似

        static public CustObject RetrieveOrderDetails(int para)
        {
                foreach (KeyValuePair<string, CustObject> pair in static_container)
                {                   
                    if (pair.Value.cust_id.Equals(symbol))
                    {
                        if (pair.Value.OrderStatus != para) 
                        {
                           return pair.Value; //Found
                        }
                    }
                }
                return null; //Not found
        }
    

    这些变化是否会解决我所遇到的异常。

    编辑:

    它在此页面上声明GetEnumerator方法允许您在写入的同时遍历元素(尽管它可能已过时)。 是不是像使用foreach一样?


    在做foreach()之前foreach()尝试将容器复制到一个新实例

    var unboundContainer = static_container.ToList();
    foreach (KeyValuePair<string, CustObject> pair in unboundContainer)
    

    另外我认为从线程安全的角度来看,更新Value属性是不正确的,重构你的代码来使用TryUpdate()来代替。


    对于元素的修改,一种选择是使用for循环手动迭代字典,例如:

    Dictionary<string, string> test = new Dictionary<string, string>();
    int dictionaryLength = test.Count();
    
    for (int i = 0; i < dictionaryLength; i++)
    {
        test[test.ElementAt(i).Key] = "Some new content";
    }
    

    尽管如此,如果你还添加到词典中,你必须适当地增加dictionaryLength(或者如果你移动元素就递减)。

    取决于你在做什么,如果订单很重要,你可能希望使用SortedDictionary代替。

    你可以通过在每次迭代时调用test.Count()来显式更新dictionaryLength来扩展它,并且还可以使用包含已经修改过的键的列表的附加列表等等,如果存在缺失的危险,真的取决于你在做什么以及你的需求是什么。

    你可以进一步使用test.Keys.ToList()得到一个键列表,该选项的工作方式如下:

    Dictionary<string, string> test = new Dictionary<string, string>();
    List<string> keys = test.Keys.ToList();
    foreach (string key in keys)
    {
        test[key] = "Some new content";
    }
    
    IEnumerable<string> newKeys = test.Keys.ToList().Except(keys);
    
    if(newKeys.Count() > 0)
        // Do it again or whatever.
    

    请注意,我还展示了一个示例,了解如何在获取初始密钥列表之间添加新密钥,并完成迭代,以便循环并处理新密钥。

    希望这些选项中的一个适合你(或者你甚至可能想要混合和匹配 - 例如在更新时按键更新,而不是长度) - 正如我所说,这与你正在尝试的内容一样多尽可能地做任何事情。

    链接地址: http://www.djcxy.com/p/69499.html

    上一篇: Thread Safe Collection Modification

    下一篇: serializing object in MVC4 action results in null values