2013-04-30 141 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