收藏已修改;枚举操作可能无法执行。到处都在使用锁怎么可能?

Posted

技术标签:

【中文标题】收藏已修改;枚举操作可能无法执行。到处都在使用锁怎么可能?【英文标题】:Collection was modified; enumeration operation may not execute. Lock is being used everywhere how possible? 【发布时间】:2013-05-21 12:43:44 【问题描述】:

这是一个只有我在编写和使用的小程序。

现在我将编写所有使用导致此问题的哈希集的区域的代码

我不明白这怎么可能。此项目仅在 MainWindow 中使用

hsProxyList 是一个哈希集

  HashSet<string> hsProxyList = new HashSet<string>();

错误发生在下面的迭代中

 lock (hsProxyList)
            
    int irRandomProxyNumber = GenerateRandomValue.GenerateRandomValueMin(hsProxyList.Count, 0);
    int irLocalCounter = 0;
    foreach (var vrProxy in hsProxyList)
    
       if (irLocalCounter == irRandomProxyNumber)
       
       srSelectedProxy = vrProxy;
       break;
       
         irLocalCounter++;
       
    

我使用 hsProxyList 的其他地方

当我得到它的计数时我没有锁定对象 - 我想这不会导致任何错误但可能不正确 - 不是致命的重要

 lblProxyCount.Content = "remaining proxy count: " + hsProxyList.Count;

新的

lock (hsProxyList)

    hsProxyList.Remove(srSelectedProxy);

新的

lock (hsProxyList)

    hsProxyList = new HashSet<string>();
    foreach (var vrLine in File.ReadLines(cmbBoxSelectProxy.SelectedItem.ToString()))
    
        hsProxyList.Add(vrLine);
    

可以看出,我到处都在使用锁。这是一个多线程软件。所有 hsProxyList 都在 MainWindow.xaml.cs 中使用 - 它是一个 C# WPF 应用程序

【问题讨论】:

堆栈跟踪是什么? 您能提供一个完整的可编译示例吗? @SLaks 它没有显示。我怎样才能让它显示这种错误? 你用的是什么IDE? @Default 我使用的是 Visual Studio 2012 【参考方案1】:

这里有两个问题。首先,已经指出的是,您锁定了哈希集,同时还将对象 hsProxyList 更改为:

lock (hsProxyList)

    hsProxyList = new HashSet<string>();
    // hsProxyList is no longer locked.

第二个(也是更微妙的)问题是您假设Count 不需要锁。这不是一个安全的假设。首先,你不知道HashSet 是如何实现它的。 Count 是 O(1) 操作这一事实表明存在一个成员变量来跟踪计数。这意味着在AddRemove 上必须更新此变量。 Add 的实现可能类似于:

bool Add( T item ) 
    this.count++;
    // Point A.
    addItemToHashSet(item);

请注意,count 变量会递增,然后会添加项目。如果调用 Add 的线程在 A 点 被中断,并且您的另一个调用 Count 的线程被执行,您将收到一个高于实际元素数量的计数(count 已被增加,但addItemToHashSet 没有)。

这可能不会产生任何严重后果,但如果您正在迭代 Count 元素,则可能会导致崩溃。调用Remove 时也可能出现类似行为。

【讨论】:

谢谢史蒂夫。所以你的意思是只得到计数可能会使应用程序崩溃我理解正确吗?我知道这个数字可能不正确,但对我来说并不重要。在上述答案软件也可以完美运行几个小时后,所以我想这就是问题所在。 @MonsterMMORPG 更改对象上的锁定肯定是您的错误消息的原因。我想让您了解一个更微妙的问题,它不会生成错误消息,但可能会给您带来奇怪且难以重现的行为。因为您只是显示Count,所以几乎可以肯定不会引起问题。但是,您应该知道,在很多情况下这样做并不安全。【参考方案2】:

问题出在哪里

lock (hsProxyList)

    hsProxyList = new HashSet<string>();
    // etc

所有锁都在一个特定对象上,但是当您执行hsProxyList = new HashSet&lt;string&gt;(); 时您正在更改对象,因此变量 hsProxyList 所引用的对象不再被锁定。

【讨论】:

非常有效的观点 :) 这可能就是为什么它不会一直发生但很少发生的原因。 据此更改了代码。相反,我将使用新的 clear。现在让我们看看:) 好吧,对于那些可能想知道的人来说,这解决了我的问题 :) 再次感谢。

以上是关于收藏已修改;枚举操作可能无法执行。到处都在使用锁怎么可能?的主要内容,如果未能解决你的问题,请参考以下文章

收藏已修改;枚举操作可能无法执行

收藏已修改;枚举操作可能无法执行

收藏已修改;枚举操作可能无法执行(带锁的多线程)

C# 使用定时器和Dictionary 出现异常说集合已修改,可能无法执行枚举操作,求帮忙啊

C# 集合已修改;可能无法执行枚举操作

C# - 集合已修改;枚举操作可能无法执行