2013-04-30 77 views
2

我有一本字典,如果字典中的項目經過我想要刪除它的所有處理。while循環中的字典和刪除字典中的項目

  var dictEnum = dictObj.GetEnumerator(); 
      while (dictEnum.MoveNext()) 
      { 
       Parallel.ForEach(dictObj, pOpt, (KVP, loopState) => 
       { 
         processAndRemove(KVP.Key); 
       }); 
      } 

      private void processAndRemove(string keyId) 
      { 
       try 
       { 
       <does stuff> 
       dictObj.Remove(keyId); 
       } catch(exception ex) { 
       ... 
       <does not remove anything, wants to retry until it doesn't fail> 
       } 
      } 

我想讓循環繼續處理字典中的所有剩餘項目(未刪除)。

但是,我收到一個錯誤。當我運行此代碼的更簡單版本時,我收到一條消息:

集合已被修改;枚舉操作可能不會執行

有沒有辦法使用字典來做到這一點?

更新:

爲了給更多的上下文。這背後的想法是如果循環繼續運行,如果有dictObj中剩下的項目。所以如果我以10和8開始,我想重新運行直到他們沒有通過的2。

回答

0

,從我從有談話:葉普斯蒂格尼爾森催生了一個主意,嘗試ConcurrentDictionary

這是我的測試代碼,我能夠從詞典中刪除的項目(從Parallel.Foreach環內)和while循環繼續,直到count == 0 or the retryAttempts > 5

public static ConcurrentDictionary<string, myRule> ccDict= new ConcurrentDictionary<string, myRule>(); 
     try 
     { 
      while (ccDict.Count > 0) 
      { 
       Parallel.ForEach(ccDict, pOptions, (KVP, loopState) => 
       { 
        //This is the flag that tells the loop do exit out of loop if a cancellation has been requested 
        pOptions.CancellationToken.ThrowIfCancellationRequested(); 
        processRule(KVP.Key, KVP.Value, loopState); 
       }); //End of Parallel.ForEach loop 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message.ToString()); 
      Console.ReadLine(); 
     } 

    public static int processRule(string rId, myRule rule, ParallelLoopState loopState) 
    { 
     try 
     { 
      if (rId == "001" || rId == "002") 
      { 
       if (rId == "001" && ccDict[rId].RetryAttempts == 2) 
       { 
        operationPassed(rId); 
        return 0; 
       } 
       operationFailed(rId); 
      } 
      else 
      { 
       operationPassed(rId); 
      } 
      return 0; 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("failed : " + ex.Message.ToString()); 
      return -99; 
     } 
    } 

    private static void operationPassed(string rId) 
    { 
     //Normal Operation 
     ccDict[rId].RulePassed = true; 
     ccDict[rId].ExceptionMessage = ""; 
     ccDict[rId].ReturnCode = 0; 

     Console.WriteLine("passed: " + rId + " Retry Attempts : " + ccDict[rId].RetryAttempts.ToString()); 

     rule value; 
     ccDict.TryRemove(rId, out value); 
    } 

    private static void operationFailed(string ruleId) 
    { 
     //This acts as if an EXCEPTION has OCCURED 
     int retryCount = 0; 

      ccDict[rId].RulePassed = false; 
      ccDict[rId].RetryAttempts = ccDict[rId].RetryAttempts + 1; 
      ccDict[rId].ExceptionMessage = "Forced Fail"; 
      ccDict[rId].ReturnCode = -99; 

      ccDict.TryUpdate(rId, ccDict[rId], ccDict[rId]); 

      if (ccDict[rId].RetryAttempts >= 5) 
      { 
       Console.WriteLine("Failed: " + rId + " Retry Attempts : " + ccDict[rId].RetryAttempts.ToString() + " : " + ccDict[rId].ExceptionMessage.ToString()); 
       cancelToken.Cancel(); 
      } 
    } 

    public class myRule 
    { 
     public Boolean RulePassed = true; 
     public string ExceptionMessage = ""; 
     public int RetryAttempts = 0; 
     public int ReturnCode = 0; 


     public myRule() 
     { 
      RulePassed = false; 
      ExceptionMessage = ""; 
      RetryAttempts = 0; 
      ReturnCode = 0; 
     } 
    } 
3

如果您在同一時間迭代它,則無法從集合中刪除項目。 但是,您可以執行的操作是將要移除的所有元素存儲在單獨的集合中。

然後,當您完成枚舉時,您可以遍歷列表以從原始集合中刪除每個項目。

或者,查看Best way to remove multiple items matching a predicate from a c# Dictionary?。好漂亮啊。接受的答案摘錄,用戶@JaredPar提供的是:

foreach (var s in MyCollection.Where(p => p.Value.Member == foo).ToList()) { 
    MyCollection.Remove(s.Key); 
} 
+0

任務雖然是保持進程運行,直到我在字典(或其他容器)沒有更多的項目。我所要求的要求在一個過程中具有。 – webdad3 2013-04-30 22:29:34

1

開始更多信息的第二收集和要保持增加值到它。

4

正如Jalayn所說,在列舉它時不能從集合中刪除。您必須重寫代碼,以便將其添加到其他集合,然後枚舉該集合並從原始集合中刪除項目。

喜歡的東西:

var toRemove = new Dictionary<int, string>() //whatever type it is 

Parallel.ForEach(dictObj, pOpt, (KVP, loopState) => 
{ 
    toRemove.Add(KVP); 
}); 

foreach (var item in toRemove) 
{ 
    dictObject.Remove(item.Key); 
} 
+0

這樣可以讓問題保持奇怪的'while'循環,其中'.MoveNext()'被調用,但'.Current'永遠不會被讀取。杜你看到'while'循環應該是什麼意思?如果'dictObj'的初始計數爲100,是否應該有100個'Parallel.ForEach'方法調用或者什麼? ___編輯:___另外,在_parallel_循環中添加到'Dictionary <,>'類型的'toRemove'字典是否安全?它不是線程安全的? – 2013-04-30 22:14:05

+0

@JeppeStigNielsen你是對的,刪除'while'。我試圖不把重點放在屠殺OP的代碼上,只是回答他關於錯誤的問題:)。至於那條平行評論,我的PLINQ並不是那麼棒,但如果你可以進一步闡述這個問題,可以隨時編輯我的帖子。 – mattytommo 2013-04-30 22:25:20

+0

看到我更新的問題。這是否回答了一個過程問題? – webdad3 2013-04-30 22:30:26

1

你爲什麼叫GetEnumerator()明確,而不是使用foreach的? foreach聲明可以幫助你。在這種情況下,您在循環中使用MoveNext(),但您從不讀Current屬性。

它看起來像你試圖在你的dictObj上使用Parallel.ForEach,但你確定它是一個線程安全的類型?可能不會。它的類型是什麼?

最後,錯誤文本自身說明。你不能修改你正在迭代的同一個集合。

+0

從我的研究中,你需要字典在做一個while循環時有一個枚舉器。 – webdad3 2013-04-30 22:02:20

+0

我正在探索使用while循環,以便它將繼續遍歷所有失敗的項目,而不必啓動另一個進程。 – webdad3 2013-04-30 22:04:40

+0

@JeffV「dictObj」字段的_type_是什麼? – 2013-04-30 22:20:56