列表线程安全
我正在使用下面的代码
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));
});
谢谢。
I am using the below code
var processed = new List<Guid>();
Parallel.ForEach(items, item =>
{
processed.Add(SomeProcessingFunc(item));
});
Is the above code thread safe? Is there a chance of processed list getting corrupted? Or should i use a lock before adding?
var processed = new List<Guid>();
Parallel.ForEach(items, item =>
{
lock(items.SyncRoot)
processed.Add(SomeProcessingFunc(item));
});
thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
不!它根本不安全,因为
processed.Add
不安全。您可以执行以下操作:请记住,
Parallel.ForEach
主要是为序列中每个元素的命令操作而创建的。您所做的是映射:投影序列的每个值。这就是Select
的创建目的。AsParallel
以最有效的方式跨线程扩展它。这段代码工作正常:
但在多线程方面没有意义。每次迭代时的锁定都会强制完全顺序执行,一堆线程将等待单个线程。
No! It is not safe at all, because
processed.Add
is not. You can do following:Keep in mind that
Parallel.ForEach
was created mostly for imperative operations for each element of sequence. What you do is map: project each value of sequence. That is whatSelect
was created for.AsParallel
scales it across threads in most efficient manner.This code works correctly:
but makes no sense in terms of multithreading.
lock
ing at each iteration forces totally sequential execution, bunch of threads will be waiting for single thread.使用:
请参阅并行 foreach 循环 - 奇怪的行为。
Use:
See parallel foreach loop - odd behavior.
来自 Jon Skeet 的书 C# 深入了解:
其中包括:
IProducerConsumerCollection
BlockingCollection
ConcurrentBag
ConcurrentQueue
ConcurrentStack
ConcurrentDictionary
From Jon Skeet's Book C# in Depth:
These include:
IProducerConsumerCollection<T>
BlockingCollection<T>
ConcurrentBag<T>
ConcurrentQueue<T>
ConcurrentStack<T>
ConcurrentDictionary<TKey, TValue>
作为 Andrey 的 answer 的替代方案:
您也可以写
这使得后面的查询更加高效,因为没有合并是必需的,MSDN。
确保
SomeProcessingFunc
函数是线程安全的。我认为,但没有测试它,如果可以在其他线程中修改列表(添加或删除)元素,您仍然需要锁。
As alternative to the answer of Andrey:
You could also write
This makes the query that is behind it even more efficient because no merge is required, MSDN.
Make sure the
SomeProcessingFunc
function is thread-safe.And I think, but didn't test it, that you still need a lock if the list can be modified in an other thread (adding or removing) elements.
尽管有上述所有可能的更有效的建议,您仍然可以改进您的工作代码:
...通过仅在将项目添加到您的收藏时防止竞争条件,从而通过以下简单的更改限制您获得对共享资源的独占访问权的时间:
Despite all of the above, possibly, more effective suggestions, you could still improve your working code:
...by only preventing racing conditions when adding items to your collection, thus limiting the time you get exclusive access to the shared resource with this simple change:
读取是线程安全的,但添加则不是。您需要一个读取器/写入器锁定设置,因为添加可能会导致内部数组调整大小,从而扰乱并发读取。
如果您可以保证数组在添加时不会调整大小,那么您在阅读时可以安全地添加,但不要引用我的观点。
但实际上,列表只是数组的接口。
reading is thread safe, but adding is not. You need a reader/writer lock setup as adding may cause the internal array to resize which would mess up a concurrent read.
If you can guarantee the array won't resize on add, you may be safe to add while reading, but don't quote me on that.
But really, a list is just an interface to an array.
使用 Something 类型的 ConcurrentBag
Using ConcurrentBag of type Something