列出<t>线程安全性</t>

时间:2011-02-16 18:22:29

标签: c# list c#-4.0 task-parallel-library parallel-extensions

我正在使用以下代码

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

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

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

感谢。

6 个答案:

答案 0 :(得分:28)

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

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

请注意,Parallel.ForEach主要是为每个序列元素的命令式操作而创建的。你要做的是map:项目序列的每个值。这就是为Select创建的内容。 AsParallel以最有效的方式跨线程扩展它。

此代码可正常运行:

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

但在多线程方面毫无意义。 lock在每次迭代时强制完全顺序执行,一堆线程将等待单线程。

答案 1 :(得分:6)

使用:

var processed = new ConcurrentBag<Guid>();

请参阅parallel foreach loop - odd behavior

答案 2 :(得分:4)

在他到达之前引用Jon Skeet

  

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

其中包括IProducerConsumerCollection<T>, BlockingCollection<T>, ConcurrentBag<T>, ConcurrentQueue<T>, ConcurrentStack<T>, and ConcurrentDictionary<TKey, TValue>等。

答案 3 :(得分:1)

替代安德烈的answer

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

你也可以写

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

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

答案 4 :(得分:1)

使用Something类型的ConcurrentBag

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

答案 5 :(得分:0)

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

如果您可以保证数组在添加时不会调整大小,则可以在阅读时添加,但不要引用我。

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