2011-02-16 49 views
22

我使用下面的代码列表<T>线程安全

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)); 
}); 

谢谢。

+0

的ConcurrentBag你看MSDN?这里:http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#c9721fa0-1cd9-4a21-818c-98d164c9fc14 – 2011-02-16 18:25:42

+1

请参阅http://stackoverflow.com/questions/4779165/parallel-foreach-loop余的行为。 – mellamokb 2011-02-16 18:26:05

+0

@Martinho:是的。我读到List 不是线程安全的。但我无法理解,即使多个线程正在添加到列表中,也会损坏列表。 – stackoverflowuser 2011-02-16 18:41:11

回答

23

不!它根本不安全,因为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)); 
}); 

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

4

Jon Skeet引述他来之前:

由于中并行扩展在.net 4的一部分,有几个新的集合 在新System.Collections.Concurrent 命名空间。这些被设计为 安全,面对来自多个线程的并发 操作,其中 锁定相对较少。

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

0

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

如果您可以保证数组不会在添加时调整大小,您在阅读时可能会很安全,但请不要在此引用我的意思。

但真的,列表只是一个数组的接口。

0

作为替代安德烈的answer

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

你也可以写

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

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

1

使用类型的东西

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