Reputation: 26331
I have the following code:
const int MAX = 101;
const int MIN_COMBINATORIC = 1000000;
int[,] pascalTriangle = new int[MAX, MAX];
Parallel.For(0, MAX, i =>
{
pascalTriangle[i, 0] = 1;
pascalTriangle[i, i] = 1;
});
int counter = 0;
Parallel.For(0, MAX, x => Parallel.For(1, x, y =>
{
int value = pascalTriangle[x - 1, y] + pascalTriangle[x - 1, y - 1];
Interlocked.Exchange(ref pascalTriangle[x, y], value < MIN_COMBINATORIC ? value : MIN_COMBINATORIC);
if(value > MIN_COMBINATORIC)
Interlocked.Increment(ref counter);
}));
Console.WriteLine("Result: {0}", counter);
The problem is that it sometimes prints out the correct answer (Result: 4075
), but sometimes it prints a random (and wrong) answer, such as:
Result: 2076
Result: 1771
Result: 0
I'm guessing it has something to do with the fact that I'm reading and writing a shared array between multiple threads.
As you can see, I tried adding Interlocked.Exhange()
for thread-safe write operations, but I could not find a similar method for reading (There's a Interlocked.Read()
but it can only read long
variables)
How can I make the above code run concurrently in a thread-safe manner?
Upvotes: 0
Views: 1168
Reputation: 181
The value variable in
int value = pascalTriangle[x - 1, y] + pascalTriangle[x - 1, y - 1];
is dependent on two items in the array. Due to concurrency, those values can change between adding those two items, storing them into value
, and passing that value into Interlocked.Exchange
. This means that by the time you store value
in pascalTriangle[x, y]
, the values in pascalTriangle[x - 1, y]
and/or pascalTriangle[x - 1, y - 1]
could have changed.
I actually can't think of an easy concurrent solution for this. The obvious solution is to not do this concurrently. If this the actual code you want to use, there is no real benefit to running this concurrently since it is only looping 101 * 101 = 10,201 times, which can be done very quickly (the first parallel code to initialize the triangle is only looping 101 times, so that can be single-threaded too). If you are creating multiple triangles like this, then you might benefit from concurrently creating triangles (in other words, this method that creates a triangle is single-threaded, but the caller calls this method multiple times on separate threads).
If you really want a concurrent solution, you will need to figure out how to implement a locking mechanism that can lock the 3 array items you are accessing, and (the difficult part) lock them in such a way that deadlocking cannot occur. And even if you come up with a solution, the overhead of all of the locks might actually make the code slower than doing it non-concurrently.
Upvotes: 3
Reputation: 656
You should look into using a BlockingCollection in the Systems.Collections.Concurrent
namespace.
If BlockingCollection doesn't fit your needs this namespace has a bunch of different types of collections that are all thread-safe like a dictionary, stack, queue, etc.
Upvotes: 0