Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding to a list in a Parallel.ForEach loop in a threadsafe manner

I have a bit of code that works like this on a list of obj objects called ListofObjects:

List<SomeObject> NewListofObjects<SomeObject>();

Parallel.ForEach(ListofObjects, obj =>

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

NewListofObjects.Add(newobj);

);

Now I am out of the Parallel.ForEach loop, and I want to do an operation on NewListofObjects. However, I get this error when I try to: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt".

Is this because my NewListofObjects.Add(newobj) method is not threadsafe? If so, how can I make it threadsafe?

like image 224
Conor Avatar asked Jul 02 '13 02:07

Conor


3 Answers

Is this because my NewListofObjects.Add(newobj) method is not threadsafe?

Correct. It is not threadsafe.

Any instance members are not guaranteed to be thread safe.

That's from MSDN referring to List<T> (scroll to the section titled "Thread Safety").

If so, how can I make it threadsafe?

Use a concurrent collection, like ConcurrentBag<T>. Note that you lose the ability to keep track of the order that items were inserted.

like image 73
jason Avatar answered Nov 17 '22 05:11

jason


You can use the locking block like the following code to insert items into your list in a thread-safe manner.

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...
    });
like image 45
ajawad987 Avatar answered Nov 17 '22 04:11

ajawad987


The .NET Framework 4 introduces the System.Collections.Concurrent namespace, which includes several collection classes that are both thread-safe and scalable. https://learn.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);
});
like image 1
Pažout Avatar answered Nov 17 '22 04:11

Pažout