C# 使用并发字典-线程安全集合修改

C# 使用并发字典-线程安全集合修改,c#,concurrency,C#,Concurrency,最近,我在使用通用词典时遇到了以下异常 发生了InvalidOperation异常。修改了一个集合 我意识到这个错误主要是因为我使用的静态字典上的线程安全问题 一点背景:我目前有一个应用程序,它有3种不同的方法与这个问题相关 方法A使用foreach遍历字典并返回一个值 方法B将数据添加到字典中 方法C更改字典中键的值 有时在遍历字典时,也会添加数据,这就是问题的原因。我在迭代字典内容的代码的foreach部分不断得到这个异常。为了解决这个问题,我用ConcurrentDictionary替换了

最近,我在使用通用词典时遇到了以下异常

发生了InvalidOperation异常。修改了一个集合

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

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

  • 方法A使用
    foreach
    遍历字典并返回一个值
  • 方法B将数据添加到字典中
  • 方法C更改字典中键的值
  • 有时在遍历字典时,也会添加数据,这就是问题的原因。我在迭代字典内容的代码的
    foreach
    部分不断得到这个异常。为了解决这个问题,我用
    ConcurrentDictionary
    替换了generic dictionary,下面是我所做工作的细节

    目标:我的主要目标是完全消除异常

    对于方法B(它向字典中添加了一个新键),我将
    替换为
    TryAdd

    对于方法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;
      }
    
    静态公共int-ChangeContent(int-para)
    {
    foreach(静态容器中的KeyValuePair对)
    {
    if(pair.Value.propA!=para)//挂起取消
    {
    pair.Value.data\u id=prim\u id;//我正在更新内容
    返回0;
    }
    }
    返回-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
        }
    
    static public CustObject RetrieveOrderDetails(int para)
    {
    foreach(静态容器中的KeyValuePair对)
    {                   
    if(pair.Value.cust_id.Equals(符号))
    {
    if(pair.Value.OrderStatus!=段落)
    {
    返回pair.Value;//找到
    }
    }
    }
    返回null;//未找到
    }
    
    这些更改是否将解决我遇到的异常

    编辑:

    它声明方法
    GetEnumerator
    允许您在写入的同时遍历元素(尽管它可能已经过时)。这和使用foreach不一样吗

    在执行
    foreach()
    之前,请尝试将容器复制到新实例

    var unboundContainer = static_container.ToList();
    foreach (KeyValuePair<string, CustObject> pair in unboundContainer)
    
    var unboundContainer=static_container.ToList();
    foreach(未绑定容器中的KeyValuePair对)
    

    另外,我认为从线程安全的角度来看,更新
    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";
    }
    
    字典测试=新建字典();
    int dictionaryLength=test.Count();
    对于(int i=0;i
    但是,如果还要添加到字典中,则必须适当地增加dictionaryLength(或者如果移动元素,则减少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.
    
    字典测试=新建字典();
    List keys=test.keys.ToList();
    foreach(字符串键入键)
    {
    测试[键]=“一些新内容”;
    }
    IEnumerable newKeys=test.Keys.ToList()。除了(键);
    如果(newKeys.Count()>0)
    //再做一次或者别的什么。
    
    请注意,我还展示了一个示例,说明了如何找出在获取初始键列表和完成迭代之间是否添加了任何新键,这样您就可以循环并处理新键


    希望这些选项中的一个会适合(或者你甚至可能想要混合和匹配-例如,在你前进时更新循环,而不是长度)-正如我所说的,这与你正试图做的和任何事情一样重要。

    foeach正是这样做的-调用GetEnumerator/MoveNext/Current。请显示修改字典内容的第三种方法的代码,您在哪一行得到了异常?您还没有尝试新方法,我说的对吗?@sll这里有一个答案,建议我使用
    .ToList()
    ,然后迭代结果。我猜它已经被删除了。我刚刚取消了我的答案,你能确认这有帮助吗?使用常规的for循环,你有可能重复同一个键两次(或者更多,根据删除/添加的次数)。想象一下下面的场景:当突然添加一个元素时,i的值为5(10)。现在字典不能保证元素的顺序,所以有可能在元素之前添加项。现在,在下一次迭代中,您将使用相同的键/值对!事实上,正如我所说,在这种情况下,您可能需要一个SortedDictionary,或者某种跟踪(即,已经迭代的元素列表),但在某些情况下,这完全取决于他试图实现的目标