列出线程安全

我使用以下代码

var processed = new List(); Parallel.ForEach(items, item => { processed.Add(SomeProcessingFunc(item)); }); 

上面的代码线程安全吗? 处理后的列表是否有可能被破坏? 或者我应该在添加之前使用锁?

 var processed = new List(); Parallel.ForEach(items, item => { lock(items.SyncRoot) processed.Add(SomeProcessingFunc(item)); }); 

谢谢。

没有! 它根本不安全,因为processed.Add不是。 你可以这样做:

 items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList(); 

请记住, Parallel.ForEach主要是为序列的每个元素的命令式操作而创建的。 你要做的是map:项目序列的每个值。 这就是为其创建的SelectAsParallel以最有效的方式跨线程缩放它。

此代码正常工作:

 var processed = new List(); Parallel.ForEach(items, item => { lock(items.SyncRoot) processed.Add(SomeProcessingFunc(item)); }); 

但就multithreading而言毫无意义。 lock每次迭代强制完全顺序执行,一堆线程将等待单线程。

使用:

 var processed = new ConcurrentBag(); 

参见并行foreach循环 – 奇怪的行为 。

在Jon Skeet到达之前引用它:

作为.Net 4中Parellel Extensions的一部分,新的System.Collections.Concurrent命名空间中有几个新集合。 这些设计在面对来自多个线程的并发操作时是安全的,锁定相对较少。

这些包括IProducerConsumerCollection, BlockingCollection, ConcurrentBag, ConcurrentQueue, ConcurrentStack, and ConcurrentDictionary等。

作为安德烈答案的替代方案:

 items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList(); 

你也可以写

 items.AsParallel().ForAll(item => SomeProcessingFunc(item)); 

这使得它后面的查询更加高效,因为不需要合并, MSDN 。 确保SomeProcessingFunc函数是线程安全的。 而且我认为,但是没有测试它,如果可以在其他线程(添加或删除)元素中修改列表,则仍需要锁定。

使用Something类型的ConcurrentBag

 var bag = new ConcurrentBag>; var items = GetAllItemsINeed(); Parallel.For(items,i => { bag.Add(i.DoSomethingInEachI()); }); 

读取是线程安全的,但添加不是。 您需要读取器/写入器锁定设置,因为添加可能会导致内部arraysresize,从而导致并发读取混乱。

如果你可以保证arrays不会在添加时resize,你可以安全地在阅读时添加,但不要引用我。

但实际上,列表只是数组的接口。