Добавление в список в цикле Parallel.ForEach потокобезопасным образом

У меня есть немного кода, который работает именно так в списке объектов obj, называемых ListofObjects:

List<SomeObject> NewListofObjects<SomeObject>();

Parallel.ForEach(ListofObjects, obj =>

//Do some operations here on obj to get a newobj

NewListofObjects.Add(newobj);

);

Теперь я вышел из цикла Parallel.ForEach и хочу выполнить операцию над NewListofObjects. Тем не менее, я получаю эту ошибку, когда пытаюсь: "Попытка читать или записывать защищенную память. Это часто свидетельствует о том, что другая память повреждена".

Это потому, что мой метод NewListofObjects.Add(newobj) не является потокобезопасным? Если да, то как я могу сделать это потокобезопасным?

Ответ 1

Это потому, что мой метод NewListofObjects.Add(newobj) не является потокобезопасным?

Правильно. Это не потокобезопасно.

Любые члены экземпляра не гарантируют безопасность потоков.

Что из MSDN, ссылаясь на List<T> (перейдите к разделу под названием "Безопасность потока" ).

Если да, то как я могу сделать это потокобезопасным?

Используйте параллельную коллекцию, например ConcurrentBag<T>. Обратите внимание, что вы теряете возможность отслеживать порядок вставки элементов.

Ответ 2

Вы можете использовать блок locking, такой как следующий код, чтобы вставить элементы в свой список поточно-безопасным способом.

var sync = new object();
var myNewList = new List<SomeObject>();
Parallel.ForEach(myListOfSomethings, a =>
    {
        // Some other code...
        var someObj = new SomeObject();
        // More other code...
        lock(sync)
        {
            myNewList.Add(someObj);
        }
        // Even more code...
    });

Ответ 3

.NET Framework 4 представляет пространство имен System.Collections.Concurrent, которое включает в себя несколько классов коллекций, как поточно-ориентированных, так и масштабируемых. https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/

      BlockingCollection<int>[] sourceArrays = new BlockingCollection<int>[5];
  for(int i = 0; i < sourceArrays.Length; i++)
      sourceArrays[i] = new BlockingCollection<int>(500);
  Parallel.For(0, sourceArrays.Length * 500, (j) =>
                      {
                          int k = BlockingCollection<int>.TryAddToAny(sourceArrays, j);
                          if(k >=0)
                              Console.WriteLine("added {0} to source data", j);
                      });