3

好的,所以我被告知此代码不是线程安全的。

#include <iostream>
#include <thread>

static int sum[5];

static int get_sum()
{
    int x=0;
    for (int j=0;j<5;++j)
        x += sum[j];
    return x;
}

static void f1(int x){
    sum[x] = 1;
    std::cout << "f" <<x << ": " << sum[x] << " : " << get_sum() << std::endl;
}

int main()  {

    for (int j=0;j<5;++j)
        sum[j] = 0;

    std::thread t0(f1, 0);
    std::thread t1(f1, 1);
    std::thread t2(f1, 2);
    std::thread t3(f1, 3);
    std::thread t4(f1, 4);

    while (get_sum() != 5)  ;

    t0.join();
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    std::cout << "final: " << get_sum() << std::endl;
}

有人可以向我解释为什么该程序可能无法完成吗?我知道 get_sum 的运行值将是不确定的,并且 cout 的输出将随机交错,但这与程序完成无关。

4

4 回答 4

12

一个值可能无法在一个线程中访问,而可能在另一个线程中被修改。这就是规则。

我们可以推测特定平台上可能出现的特定问题。但通常这并没有什么帮助,因为它让人们认为,如果他们能够解决他们认为可能出错的所有可能方式,那么他们的代码就可以了。但事实并非如此——事情可能而且确实会以您当时无法想到的方式出错。请不要开始这样想。这种推理导致的失败列表很长,不需要再找了。

这是它可能失败的一种方式:编译器可能会在循环get_sum之前内联并将所有值复制到寄存器中while,从而导致循环永远重复。这是一种合法的优化,因为编译器可以看到您的代码可以在循环期间的任何时间读取任何这些值while,因此在该循环期间不允许其他线程修改它们。

现在,如果您阅读此问题和答案,您会发现很多人无法想到这样的代码可能会出错。但这没关系,他们不必这样做。之所以存在这些规则,是因为很难,甚至不可能考虑代码可能出错的所有可能方式。所以不要让你的代码的正确性依赖于你拥有这种超人的能力。

于 2012-12-05T00:58:28.210 回答
1

该程序格式错误,因为它具有未定义的行为,因为在访问每个单独的数组元素时存在数据竞争,每个数组元素都可以同时访问f1并且getsum没有任何同步。

于 2012-12-05T01:03:06.860 回答
0

这是不安全的。因为多个线程正在访问全局总和。get_sum() 可能在其他线程写入相对位置之前或之后读取 sum[x] 不确定。

为了多线程安全,共享资源应该被锁保护,比如互斥锁。

于 2012-12-05T01:02:08.923 回答
0

数组总和有问题

于 2012-12-05T20:59:27.690 回答