轻松的原子计数器安全吗?

时间:2018-09-14 17:33:53

标签: c++ multithreading concurrency synchronization atomic

根据C ++ 11内存模型,以下代码是否保证返回计数器的期望值(40,000,000)? (仅限于x86)。

#include <atomic>
#include <thread>
using namespace std;

void ThreadProc(atomic<int>& counter)
{
    for (int i = 0; i < 10000000; i++)
        counter.fetch_add(1, memory_order_relaxed);
}

int main()
{
    #define COUNT 4
    atomic<int> counter = { 0 };
    thread threads[COUNT] = {};

    for (size_t i = 0; i < COUNT; i++)
        threads[i] = thread(ThreadProc, ref(counter));

    for (size_t i = 0; i < COUNT; i++)
        threads[i].join();

    printf("Counter: %i", counter.load(memory_order_relaxed));
    return 0;
}

特别是,松弛原子是否会进行协调,以使两个线程不会同时读取当前值,独立地递增当前值,而不会写入当前值,从而有效地丢失其中之一?

规范中的某些行似乎表明在上面的示例中计数器必须始终为40,000,000。

  

[注意:指定memory_order_relaxed的操作可以通过   关于内存排序。实现仍必须保证   对特定原子对象的任何给定原子访问都是不可分割的   关于对该对象的所有其他原子访问。 —尾注

  

原子读取-修改-写入操作应始终读取最后一个值   (按修改顺序)写了与   读取-修改-写入操作。

  

对特定原子对象M的所有修改都在某些情况下发生   特定的总顺序,称为M的修改顺序。如果A和B   是原子对象M和A之前发生的修改(如   B,则A的修改顺序应在B之前   M,定义如下。

本演讲还支持上述代码不受种族歧视的观念。 https://www.youtube.com/watch?v=KeLBd2EJLOU&feature=youtu.be&t=1h9m30s

在我看来,原子操作存在 个不可分的顺序,但我们不能保证该顺序是什么。因此,所有增量都必须“先于另一方”进行,而不能进行上述比赛。

但是随后有一些事情可能指向另一个方向:

  

实施应使原子存储对原子负载可见   在合理的时间内。

我从一位同事那里获悉,萨特的谈话中存在已知的错误。尽管我尚未找到任何相关资源。

C ++社区的多个成员比我暗示的要聪明得多,因为可以缓冲一个轻松的原子加法,以便随后的轻松的原子加法可以读取陈旧值并对其进行操作。

1 个答案:

答案 0 :(得分:3)

您问题中的代码是无种族限制的;所有增量都是有序的,保证了40000000的结果。
您问题中的参考文献包含该标准的所有相关引用。

其中原子存储应该在合理时间内可见的部分仅适用于单个存储。
在您的情况下,计数器通过原子级的read-modift-write操作递增,并且保证以修改顺序进行最新操作。

  

C ++社区的多个成员(...)暗示可以缓冲轻松的原子加法,以便随后的轻松的原子加法可以读取陈旧值并对其进行操作。

这是不可能的,只要修改是基于原子的read-modify-write操作。
如果标准不能保证可靠的结果,则原子增量将毫无用处

相关问题