List thread safety

后端 未结 6 731
一生所求
一生所求 2020-11-28 12:33

I am using the below code

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


        
相关标签:
6条回答
  • 2020-11-28 12:58

    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.

    0 讨论(0)
  • 2020-11-28 13:06

    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.

    0 讨论(0)
  • 2020-11-28 13:11

    Use:

    var processed = new ConcurrentBag<Guid>();
    

    See parallel foreach loop - odd behavior.

    0 讨论(0)
  • 2020-11-28 13:18

    From Jon Skeet's Book C# in Depth:

    As part of Parallel 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>
    • ConcurrentDictionary<TKey, TValue>
    • and others
    0 讨论(0)
  • 2020-11-28 13:18

    Using ConcurrentBag of type Something

    var bag = new ConcurrentBag<List<Something>>;
    var items = GetAllItemsINeed();
    Parallel.For(items,i =>                          
       {
          bag.Add(i.DoSomethingInEachI());
       });
    
    0 讨论(0)
  • 2020-11-28 13:20

    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. locking at each iteration forces totally sequential execution, bunch of threads will be waiting for single thread.

    0 讨论(0)
提交回复
热议问题