ConcurrentBag <MyType> Vs List <MyType>

Ben*_*Ben 21 .net c# vb.net multithreading

使用ConcurrentBag(Of MyType)对使用List(Of MyType)有什么好处? CB上的MSDN页面说明了这一点

ConcurrentBag(Of T)是一个线程安全的包实现,针对同一个线程生成和使用存储在包中的数据的场景进行了优化

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

Mik*_*ike 36

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

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

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


Jar*_*Par 17

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

另外List<T>是有序的集合而ConcurrentBag<T>不是.


Mat*_*ius 7

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"))
        {
        }
    }
Run Code Online (Sandbox Code Playgroud)

使用 ConcurrentBag 以 500 万条记录对自身运行 3 次的误差幅度

" 1: 00:00:00.4550455 2: 00:00:00.4090409" "
" 1: 00:00:00.4190419 2: 00:00:00.4730473"
"" 1: 00:00:00.4090407.03

3 运行 ConcurrentBag 与具有 5M 记录的本地锁:

" 1: 00:00:00.5070507 2: 00:00:00.3660366" "
" 1: 00:00:00.4470447 2: 00:00:00.2470247"
"" 1: 00:00:00.36603604.04

有 5000 万条记录

" 1: 00:00:04.7354735 2: 00:00:04.7554755"
" 1: 00:00:04.2094209 2: 00:00:03.2413241"

我会说本地锁稍微快一点

更新:在(至强 X5650 @ 2.67GHz 64 位 Win7 6 核)上,“本地锁定”似乎表现更好

有 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.504606:00:00:15:8046040