ConcurrentBag <mytype> Vs List <mytype> </mytype> </mytype>

时间:2010-06-01 15:15:55

标签: c# .net vb.net multithreading

使用ConcurrentBag(Of MyType)对使用List(Of MyType)有什么好处? The MSDN page on the CB表示

  

ConcurrentBag(Of T)是一个线程安全的   包实施,优化   相同线程的场景   同时生产和消费数据   存放在包里

那么有什么优势呢?我可以理解Concurrency命名空间中其他集合类型的优点,但是这个让我很困惑。

5 个答案:

答案 0 :(得分:35)

在内部,ConcurrentBag使用几个不同的列表实现,每个写入线程一个。

你引用的那句话的意思是,当从包中读取时,它将优先考虑为该线程创建的列表。这意味着,它将首先检查该线程的列表,然后再冒险争夺另一个线程的列表。

这样,当多个线程都在读写时,它可以最小化锁争用。当读取线程没有列表或其列表为空时,它必须锁定分配给不同线程的列表。但是,如果你有多个线程都在读取和写入自己的列表,那么你就不会有锁争用。

答案 1 :(得分:17)

这里最大的优点是ConcurrentBag<T>可以安全地从多个线程访问,而LisT<T>则不然。如果线程安全访问对您的方案很重要,那么像ConcurrentBag<T>这样的类型可能优于List<T> +手动锁定。在我们真正回答这个问题之前,我们需要更多地了解您的情景。

此外List<T>是有序集合,而ConcurrentBag<T>则不是。

答案 2 :(得分:4)

<强> TLDR;我会说本地锁更快但差异可以忽略不计(或者我设置我的测试)。

绩效分析:

private static IEnumerable<string> UseConcurrentBag(int count)
    {
        Func<string> getString = () => "42";

        var list = new ConcurrentBag<string>();
        Parallel.For(0, count, o => list.Add(getString()));
        return list;
    }

    private static IEnumerable<string> UseLocalLock(int count)
    {
        Func<string> getString = () => "42";
        var resultCollection = new List<string>();
        object localLockObject = new object();
        Parallel.For(0, count, () => new List<string>(), (word, state, localList) =>
        {
            localList.Add(getString());
            return localList;
        },
            (finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); }
            );

        return resultCollection;
    }

    private static void Test()
    {
        var s = string.Empty;
        var start1 = DateTime.Now;
        var list = UseConcurrentBag(5000000);
        if (list != null)
        {
            var end1 = DateTime.Now;
            s += " 1: " + end1.Subtract(start1);
        }

        var start2 = DateTime.Now;
        var list1 = UseLocalLock(5000000);
        if (list1 != null)
        {
            var end2 = DateTime.Now;
            s += " 2: " + end2.Subtract(start2);
        }

        if (!s.Contains("sdfsd"))
        {
        }
    }

使用ConcurrentBag运行3次与5M记录

的错误保证金
  

&#34; 1:00:00:00.4550455 2:00:00:00.4090409&#34;
  &#34; 1:00:00:00.4190419 2:00:00:00.4730473&#34;
  &#34; 1:00:00:00.4780478 2:00:00:00.3870387&#34;

3运行ConcurrentBag vs Local lock with 5M records:

  

&#34; 1:00:00:00.5070507 2:00:00:00.3660366&#34;
  &#34; 1:00:00:00.4470447 2:00:00:00.2470247&#34;
  &#34; 1:00:00:00.4420442 2:00:00:00.2430243&#34;

拥有50M记录

  

&#34; 1:00:00:04.7354735 2:00:00:04.7554755&#34;
  &#34; 1:00:00:04.2094209 2:00:00:03.2413241&#34;

我会说本地锁定速度略快

更新:开启(Xeon X5650 @ 2.67GHz 64位Win7 6核心)&#39;本地锁定&#39;似乎表现得更好

拥有50M记录。

  

1:00:00:09.7739773 2:00:00:06.8076807
  1:00:00:08.8858885 2:00:00:04.6184618
  1:00:00:12.5532552 2:00:00:06.4866486

答案 3 :(得分:3)

与其他并发集合不同,ConcurrentBag<T>针对单线程使用进行了优化 与List<T>不同,ConcurrentBag<T>可以同时从多个线程使用。

答案 4 :(得分:2)

我认为您应该将其视为“多个线程访问容器并且每个线程都可以生成和/或使用数据”,它绝对适用于并行场景。