Список <T>поток безопасности - PullRequest
28 голосов
/ 16 февраля 2011

Я использую приведенный ниже код

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

спасибо.

Ответы [ 6 ]

29 голосов
/ 16 февраля 2011

Нет!Это совсем не безопасно, потому что processed.Add нет.Вы можете сделать следующее:

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

Имейте в виду, что Parallel.ForEach был создан главным образом для обязательных операций для каждого элемента последовательности.То, что вы делаете, это карта: проецируйте каждое значение последовательности.Для этого и была создана Select.AsParallel наиболее эффективно масштабирует его по потокам.

Этот код работает правильно:

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

, но не имеет смысла в смысле многопоточностиlock Если на каждой итерации выполняется полностью последовательное выполнение, группа потоков будет ожидать один поток.

6 голосов
/ 16 февраля 2011

Использование:

var processed = new ConcurrentBag<Guid>();

См. параллельный цикл foreach - странное поведение .

4 голосов
/ 16 февраля 2011

Цитировать Джона Скита до того, как он попадет сюда:

Как часть расширений Parellel в .Net 4, в новом пространстве имен System.Collections.Concurrent появилось несколько новых коллекций.,Они разработаны для обеспечения безопасности при одновременных операциях из нескольких потоков с относительно небольшой блокировкой.

Среди них IProducerConsumerCollection<T>, BlockingCollection<T>, ConcurrentBag<T>, ConcurrentQueue<T>, ConcurrentStack<T>, and ConcurrentDictionary<TKey, TValue>, среди прочих.

1 голос
/ 24 января 2016

Использование ConcurrentBag типа Something

var bag = new ConcurrentBag<List<Something>>;
var items = GetAllItemsINeed();
Parallel.For(items,i =>                          
   {
      bag.Add(i.DoSomethingInEachI());
   });
1 голос
/ 07 августа 2012

Как альтернатива ответа Андрея:

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

Вы также можете написать

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

Это делает запрос, стоящий за ним, еще более эффективным, поскольку объединение не требуется, MSDN . Убедитесь, что функция SomeProcessingFunc является поточно-ориентированной. И я думаю, но не проверял, что вам все еще нужна блокировка, если список можно изменить в другом потоке (добавление или удаление) элементов.

0 голосов
/ 16 февраля 2011

чтение потокобезопасно, но добавление - нет.Вам нужна настройка блокировки чтения / записи, так как добавление может привести к изменению размера внутреннего массива, что может привести к неправильному чтению при одновременном чтении.

Если вы можете гарантировать, что размер массива не изменится при добавлении, вы можете безопасно добавлятьчитая, но не цитируйте меня об этом.

Но на самом деле, список - это просто интерфейс к массиву.

...