根据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 ++社区的多个成员比我暗示的要聪明得多,因为可以缓冲一个轻松的原子加法,以便随后的轻松的原子加法可以读取陈旧值并对其进行操作。
答案 0 :(得分:3)
您问题中的代码是无种族限制的;所有增量都是有序的,保证了40000000的结果。
您问题中的参考文献包含该标准的所有相关引用。
其中原子存储应该在合理时间内可见的部分仅适用于单个存储。
在您的情况下,计数器通过原子级的read-modift-write操作递增,并且保证以修改顺序进行最新操作。
C ++社区的多个成员(...)暗示可以缓冲轻松的原子加法,以便随后的轻松的原子加法可以读取陈旧值并对其进行操作。
这是不可能的,只要修改是基于原子的read-modify-write操作。
如果标准不能保证可靠的结果,则原子增量将毫无用处