问题
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.
回答1:
No! It is not safe at all, because processed.Add
is not. You can do following:
items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList();
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 what Select
was created for. AsParallel
scales it across threads in most efficient manner.
This code works correctly:
var processed = new List<Guid>();
Parallel.ForEach(items, item =>
{
lock(items.SyncRoot)
processed.Add(SomeProcessingFunc(item));
});
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.
回答2:
Use:
var processed = new ConcurrentBag<Guid>();
See parallel foreach loop - odd behavior.
回答3:
To quote from Jon Skeet before he gets here:
As part of Parellel Extensions in .Net 4, there are several new collections in a new
System.Collections.Concurrent
namespace. These are designed to be safe in the face of concurrent operations from multiple threads, with relatively little locking.
These include IProducerConsumerCollection<T>, BlockingCollection<T>, ConcurrentBag<T>, ConcurrentQueue<T>, ConcurrentStack<T>, and ConcurrentDictionary<TKey, TValue>
among others.
回答4:
As alternative to the answer of Andrey:
items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList();
You could also write
items.AsParallel().ForAll(item => SomeProcessingFunc(item));
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.
回答5:
Using ConcurrentBag of type Something
var bag = new ConcurrentBag<List<Something>>;
var items = GetAllItemsINeed();
Parallel.For(items,i =>
{
bag.Add(i.DoSomethingInEachI());
});
回答6:
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.
来源:https://stackoverflow.com/questions/5020486/listt-thread-safety