Добавление в список в цикле 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);
});