我有以下程序:
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()
应输出4075
,事实上,它确实...... ~80%的时间。
我正在运行以下程序:
private const int TEST_RUNS = 50;
public static void Main(String[] args)
{
Parallel.For(0, TEST_RUNS, i => Console.WriteLine(Process()));
Console.ReadLine();
}
输出如下:
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
正如您所看到的,最后的值是错误的,所以我猜Process()
根本不是线程安全的。
这是为什么?我在ConcurrentDictionary
内部使用共享Process()
,但ConcurrentDictionary
不应该是线程安全的?我的Process()
方法如何返回错误的结果?
答案 0 :(得分:2)
确定它不是线程安全的
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;
}
要修复,请按顺序运行外部循环。您的代码取决于外部for的顺序执行。
ConcurrentDictionary与此无关,您使用它的事实并不会使您的代码成为线程安全的。