【发布时间】:2015-09-16 18:37:50
【问题描述】:
我有以下程序:
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() 根本不是线程安全的。
这是为什么?我在Process() 中使用共享ConcurrentDictionary,但ConcurrentDictionary 不应该是线程安全的吗?我的Process() 方法怎么会返回错误的结果?
【问题讨论】:
-
据我所见,您的代码从 pascalTriangle 读取 y-1'st 值并更新 y'th。这会产生竞争条件,因为 y-1st 可能已在另一个线程中更新,也可能尚未更新。
-
@Slava 我知道,但
ConcurrentDictionary不是为解决这个问题而设计的吗? -
使用线程安全的类并不能使你自己的代码线程安全。
-
并发字典允许在没有锁的情况下读取/写入集合。你的算法有缺陷,ConcurrentDictionary 帮不上忙
标签: c# multithreading concurrency task-parallel-library