收藏已修改;枚举操作可能无法执行。到处都在使用锁怎么可能?
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)
操作这一事实表明存在一个成员变量来跟踪计数。这意味着在Add
或Remove
上必须更新此变量。 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<string>();
时您正在更改对象,因此变量 hsProxyList 所引用的对象不再被锁定。
【讨论】:
非常有效的观点 :) 这可能就是为什么它不会一直发生但很少发生的原因。 据此更改了代码。相反,我将使用新的 clear。现在让我们看看:) 好吧,对于那些可能想知道的人来说,这解决了我的问题 :) 再次感谢。以上是关于收藏已修改;枚举操作可能无法执行。到处都在使用锁怎么可能?的主要内容,如果未能解决你的问题,请参考以下文章