我正在使用以下代码
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 :(得分: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)
答案 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)
读取是线程安全的,但添加不是。您需要读取器/写入器锁定设置,因为添加可能会导致内部阵列调整大小,从而导致并发读取混乱。
如果您可以保证数组在添加时不会调整大小,则可以在阅读时添加,但不要引用我。
但实际上,列表只是数组的接口。