簡體   English   中英

收到“錯誤:集合已修改; 枚舉操作可能無法在多線程應用程序中執行”

[英]Receiving “Error: Collection was modified; enumeration operation may not execute” in multithreaded application

當前,我正在編寫一個定期清除單例集合的應用程序:

            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的異常ProcessInfo 如果您正在處理異常,並且潛在地不想在字典中重新處理某個項目,那么您將希望跟蹤成功處理了哪些項目並將這些項目從字典中刪除,也許在finally塊中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM