如何将项目添加到字典“并行循环安全”
Posted
技术标签:
【中文标题】如何将项目添加到字典“并行循环安全”【英文标题】:How to add item to dictionary "Parallel loop safe" 【发布时间】:2014-05-12 11:59:43 【问题描述】:我有一个 Parallel.ForEach 循环做一些处理。但是第一个操作是在不包含键的情况下在字典中添加一个值。添加它时出现错误,它说密钥已经在字典中。我猜想密钥是在 .Contains 检查这个线程之后由并行进程添加的,但在添加之前。除了将该行放在 try-catch 中之外,还有其他简单的解决方案可以用来防止该错误吗?
Parallel.ForEach(branchFixes, b =>
Parallel.ForEach(b.Value, t =>
var team = t.Key;
if (!resultTeamDict.ContainsKey(team))
resultTeamDict.Add(team, new Dictionary<FixItem, Dictionary<BranchInfo, bool>>());
);
);
【问题讨论】:
@mellamokb:这不是真的——如果你有多个内核可用,你为什么不想使用它们呢?它只有在问题自然可并行的情况下才有效,但“CPU 密集型”意味着“并行变慢”的想法是无稽之谈。 @JonSkeet:是的,你是对的。我在考虑单核。 【参考方案1】:即使除了你的竞争条件,Dictionary<,>
也不是线程安全的。您应该使用ConcurrentDictionary<,>
,在这种情况下可能使用AddOrUpdate
方法来原子地执行修改。 (我假设您也想为“嵌套”字典添加一个值。否则,请考虑 TryAdd
。)
【讨论】:
【参考方案2】:您可以使用 .NET 4.5 中的 ConcurrentDictionary 并将 ContainsKey 和 Add 方法调用替换为 TryAdd。见http://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
【讨论】:
ConcurrentDictionary 是在 .NET 4.0 中引入的。【参考方案3】:这是“检查时间到使用时间”错误的教科书示例:在检查字典是否包含密钥和 Add
调用之间,另一个线程可能已经插入了该项目,从而使Add
的前置条件。
解决方案是使用ConcurrentDictionary<T>
或通过锁或其他同步工具相互排除线程同时更新字典。
您可能想要分析您的代码以检查是否值得触发线程——在这种情况下开销可能非常高。
【讨论】:
【参考方案4】:我看到了一些关于 ConcurrentDictionary 的建议。 请注意优化和性能问题。 Dictionary 和 ConcurrentDictionary RunTime Complexity 在插入和读取数据方面存在差异(使用 ConcurrentDictionary 可能会慢 10 倍)
【讨论】:
以上是关于如何将项目添加到字典“并行循环安全”的主要内容,如果未能解决你的问题,请参考以下文章