接收"错误:收集已被修改;枚举操作不能执行.在多线程应用程序中
本文关键字:不能 操作 枚举 执行 多线程 应用程序 修改 错误 quot 接收 | 更新日期: 2023-09-27 18:09:17
目前,我正在编写一个定期抽取单例集合的应用程序:
lock (_lockItemsReceivedObject)
{
DateTime commitTime = DateTime.Now;
while (ItemsReceivedInstance.Count > 0)
{
ProcessInfo(commitTime);
}
}
这里是ProcessInfo:
private void ProcessInfo(DateTime commitTime)
{
Dictionary<Int32, Item>.Enumerator enumerator =
ItemsReceivedInstance.GetEnumerator();
if (enumerator.MoveNext())
{
Item item = enumerator.Current.Value;
// put item into persistent storage and perform other processing...
ItemsReceivedInstance.Remove(enumerator.Current.Key);
}
}
下面是关于这个异常的更多细节:
Error: Collection was modified; enumeration operation may not execute. at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
在程序的其他地方,其他线程正在接收项并将它们放入单例ItemsReceivedInstance集合中。然而,对我来说没有意义的是,由于我正在使用锁,ItemsReceivedInstance集合应该不能被修改,直到它在进程退出临界区时被清空,所以为什么我收到这个异常?有人有什么建议吗?TIA。
更新:
感谢CodeWeed和Wayne的评论。这是一种可以接受的修改集合的方式吗?
{
ConcurrentDictionary<Int32, Item>.Enumerator enumerator =
ItemsReceivedInstance.GetEnumerator();
if (enumerator.MoveNext())
{
Item item = enumerator.Current.Value;
// put item into persistent storage and perform other processing...
var itemToRemove = enumerator.Current.Key;
enumerator.Dispose();
ItemsReceivedInstance.Remove(itemToRemove);
}
}
更新2:感谢CodeWeed和Wayne,以及所有考虑过这个问题的人。foreach循环枚举器允许脏读取,因此为了获取字典的快照,我使用ToArray()(参见http://geekswithblogs.net/BlackRabbitCoder/archive/2011/02/17/c.net-little-wonders-the-concurrentdictionary.aspx),这就是我如何修改代码的:
DateTime commitTime = DateTime.Now;
foreach (KeyValuePair<Int32, Item> kvp in ItemsReceivedInstance.ToArray())
{
ProcessInfo(commitTime, kvp.Value);
}
...
private static void ProcessInfo(DateTime commitTime, Item item)
{
// put item into persistent storage and perform other processing...
}
从技术上讲,这可能可行,但我认为它仍然太做作了。为什么不:
DateTime commitTime = DateTime.Now;
foreach (var kvp in ItemsReceivedInstance)
{
ProcessInfo(commitTime, kvp);
}
ItemsReceivedInstance.Clear();
...
private static void ProcessInfo(DateTime commitTime, KeyValuePair<int, Item> kvp)
{
// put item into persistent storage and perform other processing...
}
请注意,这与您最初试图实现的目标之间的细微差异在于ProcessInfo
中异常的影响。如果您正在处理异常,并且可能不希望因此重新处理字典中的项,那么您将希望跟踪哪些项已成功处理,并从字典中删除这些项,可能是在finally
块中。