关于多线程时竞争条件的问题

Question regarding race conditions while multithreading

所以我正在阅读本章中的一本书,其中介绍了多线程和并发性,他们给了我一个对我来说没有意义的问题。

我想用参数 x 创建 3 个函数来简单地计算 x * x;一种使用互斥锁,一种使用原子类型,一种不使用。并创建 3 个保存值的全局变量。 前两个函数将防止竞争条件,但第三个可能不会。

之后我创建 N 个线程然后循环并告诉每个线程计算它的 x 函数(3 个独立的循环,每个函数一个。所以我创建了 N 个线程 3 次)

现在这本书告诉我,使用函数 1 和 2 我应该总能得到正确的答案,但使用函数 3 我不会总能得到正确的答案。但是,我总能得到所有这些问题的正确答案。我认为这是因为我只是在计算 x * x,这就是它所做的一切。

举个例子,当N=3时,正确的值为0 * 0 + 1 * 1 + 2 * 2 = 5。

这是原子函数:

void squareAtomic(atomic<int> x)
{
    accumAtomic += x * x;
}

这就是我调用函数的方式

thread threadsAtomic[N]
for (int i = 0; i < N; i++) //i will be the current thread that represents x
    {
        threadsAtomic[i] = thread(squareAtomic, i);
    }

    for (int i = 0; i < N; i++)
    {
        threadsAtomic[i].join();
    }

这是有时会产生竞争条件的函数:

void squareNormal(int x) 
{
    accumNormal += x * x;
}

我是这样称呼它的:

thread threadsNormal[N];
    for (int i = 0; i < N; i++) //i will be the current thread that represents x
    {
        threadsNormal[i] = thread(squareNormal, i);
    }

    for (int i = 0; i < N; i++)
    {
        threadsNormal[i].join();
    }

这都是我自己的代码,所以我可能没有正确地做这道题,在这种情况下我深表歉意。

竞争条件(以及一般的未定义行为)的一个问题是它们的存在并不能保证您的程序将运行不正确。相反,未定义的行为只会使您的程序 的行为按照 C++ 语言规范的规则作废。这会使未定义的行为很难通过经验测试来检测。 (每个 multithreading-programmer 最糟糕的噩梦是在程序密集 three-month 测试期间从未见过一次的错误,并且只在大型 on-stage 演示期间以神秘崩溃的形式出现在现场观众面前)

在这种情况下,您的 racy 程序的竞争条件以多个线程同时读写 accumNormal 的形式出现;特别是,如果线程 A 读取 accumNormal 的值,然后线程 B 向 accumNormal 写入一个新值,然后线程 A 向 accumNormal 写入一个新值,您可能会得到不正确的结果], 覆盖线程 B 的值。

如果您希望能够向自己证明竞争条件确实会导致不正确的结果,您会希望编写一个程序,其中多个线程长时间使用同一个共享变量。例如,您可能有一半线程将变量递增 100 万次,而另一半线程将变量递减 100 万次,然后在之后(即加入所有线程后)检查最终值是否为零(即你希望它是什么),如果不是,运行 再次测试,并在必要时让该测试 运行 通宵。 (甚至这可能不足以检测到不正确的行为,例如,如果您 运行 正在硬件上执行递增和递减,使得它们“恰好可以”用于此用例)