如何将项目添加到字典“并行循环安全”

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&lt;,&gt; 也不是线程安全的。您应该使用ConcurrentDictionary&lt;,&gt;,在这种情况下可能使用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&lt;T&gt; 或通过锁或其他同步工具相互排除线程同时更新字典。

您可能想要分析您的代码以检查是否值得触发线程——在这种情况下开销可能非常高。

【讨论】:

【参考方案4】:

我看到了一些关于 ConcurrentDictionary 的建议。 请注意优化和性能问题。 Dictionary 和 ConcurrentDictionary RunTime Complexity 在插入和读取数据方面存在差异(使用 ConcurrentDictionary 可能会慢 10 倍)

【讨论】:

以上是关于如何将项目添加到字典“并行循环安全”的主要内容,如果未能解决你的问题,请参考以下文章

如何将重复键添加到字典中

如何将字典键应用于值项目到火花数据集中的列?

如何将字典数据添加到数组中?

如何将字典的 NSDictionary 添加到 NSArray?

如何将 Numpy 数组添加到字典中

循环记录集并添加到字典/集合:项目键存在