2

我有一本字典,如果字典中的项目通过了我想要删除的所有处理。

            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 次。

4

6 回答 6

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);
}
于 2013-04-30T21:57:31.937 回答
3

如果同时对集合进行迭代,则无法从集合中删除它。但是,您可以做的是将要删除的所有元素存储在单独的集合中。

然后,当您完成枚举时,您可以遍历列表以从原始集合中删除每个项目。

或者,查看从 ac# Dictionary 中删除与谓词匹配的多个项目的最佳方法?. 这很美。由用户@JaredPar 提供的已接受答案摘录是:

foreach ( var s in MyCollection.Where(p => p.Value.Member == foo).ToList() ) {
  MyCollection.Remove(s.Key);
}
于 2013-04-30T21:53:24.953 回答
1

开始第二个集合并向其中添加要保留的值。

于 2013-04-30T21:56:48.987 回答
1

我不认为你可以用字典来做到这一点。相反,您可以执行类似的操作Dictionary.Values.ToList(),删除您想要的内容,然后调和差异。

这个问题有更多关于它的信息Collection 已修改;枚举操作可能无法执行

于 2013-04-30T21:55:06.247 回答
1

为什么要GetEnumerator()显式调用而不是使用foreach?该foreach声明可以帮助您。在这种情况下,您MoveNext()在循环中使用,但您从未读取过该Current属性。

看起来您尝试Parallel.ForEach在您的上使用dictObj,但您确定它是线程安全的类型吗?可能不是。它的类型是什么?

最后,错误文本说明了一切。您不能修改您正在迭代的同一个集合。

于 2013-04-30T21:59:10.717 回答
1

根据我的对话:Jeppe Stig Nielsen 产生了一个尝试ConcurrentDictionary

这是我的测试代码,我能够从 Dictionary 中删除项目(从 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;
        }
    }
于 2013-05-02T16:40:30.387 回答