2

我有以下面试问题:

class someClass
{
    int sum=0;
    public void foo()
    {
        for(int i=0; i<100; i++)
        {
            sum++
        }
    }
}

有两个并行线程通过 foo 方法运行。最后 sum 的值会在 100 到 200 之间变化。问题是为什么。据我了解,只有一个线程获得 cpu,并且线程在运行时被抢占。在什么情况下,干扰会导致总和未达到 200?

4

3 回答 3

10

增量不是原子的。它读取值,然后写出增加的值。在这两个操作之间,另一个线程可以以复杂的方式与 sum 交互。

值的范围实际上不是 100 到 200。这个范围是基于错误的假设,即线程要么轮流执行,要么同时执行每次读取。还有更多可能的交织,其中一些产生明显不同的值。最坏的情况如下(x表示表达式中使用的隐式临时sum++):

    Thread A            Thread B
----------------    ----------------
x     ← sum (0)
                    x     ← sum (0)
x + 1 → sum (1)
x     ← sum (1)
x + 1 → sum (2)
⋮
x     ← sum (98)
x + 1 → sum (99)
                    x + 1 → sum (1)
x     ← sum (1)
                    x     ← sum (1)
                    x + 1 → sum (2)
                    ⋮
                    x     ← sum (99)
                    x + 1 → sum (100)
x + 1 → sum (2)

因此,可能的最低值是 2。简单来说,两个线程取消了彼此的辛勤工作。你不能低于 2,因为线程 B 不能向线程 A 提供零——它只能输出一个递增的值——而线程 A 必须反过来递增线程 B 给它的 1。

于 2011-11-17T12:30:33.573 回答
3

这条线sum++是一个竞争条件。

两个线程都可以将 sum 的值读取为 0,然后每个线程将其值增加到 1 并将该值存储回来。这意味着 sum 的值将是 1 而不是 2。

像这样继续下去,你会得到 100 到 200 之间的结果。

于 2011-11-17T11:45:51.207 回答
0

现在大多数 CPU 都有多个内核。因此,如果我们在函数 foo() 的开头锁定一个互斥锁,并在 for 循环完成后解锁该互斥锁,那么在不同内核上运行的 2 个线程仍然会产生答案 200。

于 2011-11-17T22:04:25.320 回答