ConcurrentBag Vs List

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

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

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

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

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

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

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

另外List是一个有序集合,而ConcurrentBag则不是。

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

TLDR; 我会说局部锁定更快但差异可以忽略不计 (或者我设置我的测试)。

绩效分析:

 private static IEnumerable UseConcurrentBag(int count) { Func getString = () => "42"; var list = new ConcurrentBag(); Parallel.For(0, count, o => list.Add(getString())); return list; } private static IEnumerable UseLocalLock(int count) { Func getString = () => "42"; var resultCollection = new List(); object localLockObject = new object(); Parallel.For(0, count, () => new List(), (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记录相对应

“1:00:00:00.4550455 2:00:00:00.4090409”
“1:00:00:00.4190419 2:00:00:00.4730473”
“1:00:00:00.4780478 2:00:00:00.3870387”

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

“1:00:00:00.5070507 2:00:00:00.3660366”
“1:00:00:00.4470447 2:00:00:00.2470247”
“1:00:00:00.4420442 2:00:00:00.2430243”

拥有50M记录

“1:00:00:04.7354735 2:00:00:04.7554755”
“1:00:00:04.2094209 2:00:00:03.2413241”

我会说局部锁定速度略快

更新:开启(Xeon 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.5532552 2:00:00:06.4866486

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