一个读者一个编写者,int或atomic_int

时间:2016-06-02 21:40:53

标签: c++ multithreading c++11 atomic memory-fences

我知道这不是一个新问题,但在阅读了关于c ++ 11内存栅栏之后我感到困惑;

如果我有一个读者线程和一个作者线程 我可以使用普通的int吗?

    int x = 0; // global
writer    reader
x = 1;    printf("%d\n", x);

此行为是否未定义?
我可以在读者帖子中得到一个未定义的值吗? 或者就像使用std::atomic_uint_fast32_tstd::atomic<int>一样?因此,价值最终将转移到读者线程中。

    std::atomic<int x = 0; // global
writer                                    reader
x.store(1, std::memory_order_relaxed);    printf("%d\n", x.load(std::memory_order_relaxed));

答案取决于我使用的平台吗? (例如x86),所以加载/存储普通的int是一条CPU指令吗?

如果两种行为相似,我是否应该期望两种类型的表现相同?

2 个答案:

答案 0 :(得分:5)

简而言之,永远不要使用普通的int来共享多线程环境。

问题不仅在于你的CPU,还在于你的编译器的优化器。 gcc可以(并将会)优化代码,如:

while(i == 1) {}进入if(i==1) { while(1) {} }。一旦检查了变量一次,它就不必再次重新加载该值。与所有其他可能的问题分开,看到半写的值(实际上通常会在x86整数上发生)。

测量atomic的影响非常困难 - 在很多情况下,CPU可以高度优化访问,而在其他情况下,它们要慢得多。你真的必须在实践中做基准。

答案 1 :(得分:2)

使用atomics在编译器和CPU级别都有效果。正如评论所暗示的那样,你应该总是使用原子,因为否则编译器和CPU会密谋对你的代码进行疯狂和不直观的转换,使它不能达到你合理期望的效果。

你的问题的第二部分更微妙 - 使用原子而不是裸int的惩罚是什么?这当然是非常依赖于编译器和CPU的,但我们暂时假设您的编译器是&#34; smart&#34;你在英特尔CPU上。通过智能,我的意思是它不会将所有访问都包含在互斥块中,这肯定满足了原子的所有要求,但在性能上却是次优的。在Intel CPU上,您可以获得有关存储/负载可见性的某些内置保证,这使得编译器可以更轻松地执行正确的操作而无需特殊说明 - 他们只需要不优化&#34;正常&#34;行为IA64 memory ordering。虽然这并不能涵盖所有情况,但它确实可以解决您的轻松一致性问题。案件。有关详细信息,请参阅memory fencing instructions

对于具有宽松一致性的情况,英特尔没有CPU级别的惩罚,因为不需要生成围栅指令。当你需要更强的一致性时(例如在实现自旋锁或制作无锁算法时,发布顺序很重要),大多数情况下会产生惩罚。这些将导致互锁指令,围栏指令或总线锁前缀,这可能会有重大的处罚。