为什么这段代码不是线程安全的?

本文关键字:线程 安全 段代码 代码 为什么 | 更新日期: 2023-09-27 18:12:59

我有以下程序:

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()方法如何返回错误的结果?

为什么这段代码不是线程安全的?

肯定不是线程安全的

    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;
    }

要修复,请依次运行outer for循环。您的代码依赖于outer for.

的顺序执行。

ConcurrentDictionary与此无关,使用它并不会使代码线程安全。