【问题标题】:Is Parallel.ForEach in ConcurrentBag<T> thread safeConcurrentBag<T> 中的 Parallel.ForEach 线程安全吗
【发布时间】:2011-07-08 20:09:33
【问题描述】:

MSDN上对ConcurrentBag的描述不清楚:

当订购无关紧要时,包对于存储物品很有用,而且与集合不同,包支持重复。 ConcurrentBag 是一个线程安全的包实现,针对同一线程将同时生产和使用包中存储的数据的场景进行了优化。

我的问题是它是线程安全的,如果这是在 Parallel.ForEach 中使用 ConcurrentBag 的好习惯。

例如:

    private List<XYZ> MyMethod(List<MyData> myData)
    {
        var data = new ConcurrentBag<XYZ>();

        Parallel.ForEach(myData, item =>
                        {
                            // Some data manipulation

                            data.Add(new XYZ(/* constructor parameters */);
                        });

        return data.ToList();
    }

这样我就不必使用常规列表在 Parallel.ForEach 中使用同步锁定。

非常感谢。

【问题讨论】:

    标签: multithreading c#-4.0 .net-4.0 concurrency thread-safety


    【解决方案1】:

    这对我来说看起来不错。您使用它的方式是线程安全的。

    如果您可以返回 IEnumerable&lt;XYZ&gt;,完成后不复制到 List&lt;T&gt; 可以提高效率。

    【讨论】:

    • 这真的能提高性能吗? .Add 函数不会为了线程安全而同步吗? (假设注释掉的代码需要0ms才能完成。)
    • 并发集合使用无锁和部分锁定技术的混合。它们不会为每个操作锁定整个集合。
    • 感谢您的澄清。对于那些感兴趣的人,我实际上运行了一个基准测试并反编译了 CLR 代码。它使用 Interlocked.Exchange 和 Monitor.Enter。我想这就是你所说的部分锁定?无论如何,并行代码几乎每次都运行得更快,只有非常低的迭代量和小的延迟才能提高性能。我不确定我自己会使用这种技术(不要忘记注释掉的代码需要是线程安全的!),但它很快。
    【解决方案2】:

    ConcurrentBag 和 Parallel.ForEach 在我看来,没问题。如果您在具有大量多用户访问的场景中使用此类型,则您实现中的这些类可能会将 cpu 进程提升到可能使您的 Web 服务器崩溃的级别。此外,此实现启动 N 个任务(线程)来执行每个迭代,因此在选择此类和实现时要小心。我最近遇到了这种情况,我不得不提取内存转储来分析我的 Web 应用程序核心发生了什么。所以,要小心,因为 Concurrentbag 是 ThreadSafe 的,在 web 场景中它不是更好的方法。

    【讨论】:

      猜你喜欢
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多