加法运算的 C++ 线程安全

C++ Thread Safety For Addition Operation

假设您正在从 2 个不同的线程更新共享数据结构,其中操作顺序无关紧要。所以所需要的只是来自两个线程的操作的正确结果。

下面的代码在我的系统上运行良好,但我认为这不是线程安全的。

#include <thread>
#include <iostream>
#include <vector>

// A vector of [100] which all have 0 as their initial values.
std::vector<int> common(100, 0);

void add10(std::vector<int> ¶m){
    for (std::vector<int>::iterator it = param.begin();
        it != param.end(); ++it){
        *it += 10;
    }
}

void add100(std::vector<int> ¶m){
    for (std::vector<int>::iterator it = param.begin();
        it != param.end(); ++it){
        *it += 100;
    }
}


int main(int argc, char *argv[]) {

    // Print vector
    for (std::vector<int>::iterator it = common.begin();
        it != common.end(); ++it){
        std::cout << *it << std::endl;
    }
    std::cout << "==> Initial Vector" << std::endl;

    std::thread t1(add10, std::ref(common));
    std::thread t2(add100, std::ref(common));

    t1.join();
    t2.join();

    // Print vector again
    for (std::vector<int>::iterator it = common.begin();
        it != common.end(); ++it){
        std::cout << *it << std::endl;
    }

    std::cout << "==> Resulting Vector" << std::endl;
}

我在这里关心的是数据竞争;

那么在分配回向量时会不会出现数据竞争?

编辑:

对于STL数据结构,事实证明你可以像std::vector<std::atomic<int>>

一样使用它

是的,因为这些操作不是原子的。您使用迭代器获取一个值,向其添加一些内容并将该值存储回去——这背后有相当多的机器代码。另一个线程可以随时插入并替换您的值。

您必须确保此代码的线程安全。

您可以反汇编二进制文件或生成汇编代码,以查看这是如何在机器级别执行的。在 GCC 中,您可以使用 -S(注意:大写 S)开关来生成程序集列表。

如果一个线程写入一个对象,而另一个线程在没有适当同步的情况下访问(读取或写入)该对象,则会出现数据争用。如果程序包含数据竞争,则其结果是不确定的。您的程序不包含同步,但它确实并发访问共享对象,即它会导致未定义的行为。

我发现 benign data races 上的文章非常有趣:即使没有得到正确的结果是可以接受的,但事情还是会出错。

毫无疑问存在数据竞争,事实上,在 运行 你的代码两次之后,我发现结果向量的一些条目是 10,而不是 110,因为它们应该是。您可以通过 helgrind 运行 您的可执行文件立即看到这一点,即 valgrind --tool=helgrind ./race,它会告诉您确切的问题所在:

==3176== Possible data race during read of size 4 at 0x5C41040 by thread #3
==3176== Locks held: none
==3176==    at 0x4010CD: add100(std::vector<int, std::allocator<int> >&) (race.cc:19)

==3176== This conflicts with a previous write of size 4 by thread #2
==3176== Locks held: none
==3176==    at 0x40106C: add10(std::vector<int, std::allocator<int> >&) (race.cc:12)

使用std::atomic<int>,而不是ints,必要时处理delete的原子复制构造函数的问题。