I have the following program:
private const int TRIANGLE_SIZE = 101;
private const int LIMIT = 1000000;
/*
* [key] -> [value]
* [0] -> [1, 0, 0, 0, 0, 0, ...] First level
* [1] -> [1, 1, 0, 0, 0, 0 ...] Second level
* [2] -> [1, 0, 1, 0, 0, 0 ...] Third level
* [3] -> [1, 0, 0, 1, 0, 0 ...] Fourth level
* [4] -> [1, 0, 0, 0, 1, 0 ...] Fifth level
* ...
* ...
*
* Like a matrix, with TRIANGLE_SIZE dimension
*/
private static ConcurrentDictionary<int, int[]> InitPascalTriangle()
{
ConcurrentDictionary<int, int[]> pascalTriangle = new ConcurrentDictionary<int, int[]>();
Parallel.For(0, TRIANGLE_SIZE, i =>
{
int[] level = new int[TRIANGLE_SIZE];
level[0] = 1;
level[i] = 1;
pascalTriangle.TryAdd(i, level);
});
return pascalTriangle;
}
/*
* Fills the Pascal Triangle and counts the values that were bigger than LIMIT
*/
private static int Process()
{
ConcurrentDictionary<int, int[]> pascalTriangle = InitPascalTriangle();
int counter = 0;
Parallel.For(0, TRIANGLE_SIZE, y => Parallel.For(1, y, x =>
{
int[] previousLevel = pascalTriangle.GetOrAdd(y - 1, new int[TRIANGLE_SIZE]);
int value = previousLevel[x] + previousLevel[x - 1];
pascalTriangle.AddOrUpdate(y, new int[TRIANGLE_SIZE], (k, current) =>
{
current[x] = value < LIMIT ? value : LIMIT;
return current;
});
if (value > LIMIT)
Interlocked.Increment(ref counter);
}));
return counter;
}
Process()
should output 4075
, and in fact, it does... ~80% of the time.
I'm running the following program:
private const int TEST_RUNS = 50;
public static void Main(String[] args)
{
Parallel.For(0, TEST_RUNS, i => Console.WriteLine(Process()));
Console.ReadLine();
}
The output is the following:
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
4075 4075 4075 4075 4075
3799 4075 1427 4075 651
1427 681 871 871 871
As you can see, the last values are wrong, so I'm guessing Process()
is not thread safe at all.
Why is this? I'm using a shared ConcurrentDictionary
inside Process()
, but isn't ConcurrentDictionary
supposed to be thread-safe? How can wrong results be returned by my Process()
method?
Sure it's not thread safe
private static int Process() {
ConcurrentDictionary<int, IList<int>> pascalTriangle = FillPascalTriangle();
int counter = 0;
Parallel.For(0, MAX, x => {
// x goes from 1 to MAX here, in parallel
Parallel.For(1, x, y => {
// y goes from 1 to x here, in parallel
IList<int> previous;
// suppose x = 2, you got [1, ...] element here
pascalTriangle.TryGetValue(x - 1, out previous);
// now, x = 1 thread (which should calculate [1, ...] elements may or may not be run already
// Calculation based on those values it pointless
int value = previous[y] + previous[y - 1];
pascalTriangle.AddOrUpdate(x, new List<int>(), (k, current) => {
// here, x = 1 thread should update [1, ...] values, but did it already? who knows.
current[y] = value < MIN_COMBINATORIC ? value : MIN_COMBINATORIC;
return current;
});
if (value > MIN_COMBINATORIC)
Interlocked.Increment(ref counter);
});
});
return counter;
}
To fix, run outer for loop sequentially. You code depends on sequential execution of outer for.
ConcurrentDictionary has nothing to do with that, the fact you use it does not make your code thread-safe.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.