简体   繁体   English

c++ atomic:函数调用会充当内存屏障吗?

[英]c++ atomic: would function call act as memory barrier?

I'm reading this article Memory Ordering at Compile Time from which said:我正在阅读这篇文章 编译时内存排序,其中说:

In fact, the majority of function calls act as compiler barriers, whether they contain their own compiler barrier or not.This excludes inline functions, functions declared with the pure attribute, and cases where link-time code generation is used.事实上,大多数函数调用都充当编译器屏障,无论它们是否包含自己的编译器屏障。这不包括内联函数、使用 pure 属性声明的函数以及使用链接时代码生成的情况。 Other than those cases, a call to an external function is even stronger than a compiler barrier, since the compiler has no idea what the function's side effects will be.除了这些情况,对外部函数的调用甚至比编译器屏障更强,因为编译器不知道函数的副作用是什么。

Is this a true statement?这是一个真实的声明吗? Think about this sample -想想这个样本——

std::atomic_bool flag = false;
int value = 0;

void th1 () { // running in thread 1
  value = 1;
  // use atomic & release to prevent above sentence being reordered below
  flag.store(true, std::memory_order_release);
}

void th2 () { // running in thread 2
  // use atomic & acquire to prevent asset(..) being reordered above
  while (!flag.load(std::memory_order_acquire)) {}
  assert (value == 1);    // should never fail!
}

Then we can remove atomic but replace with function call -然后我们可以删除原子但替换为函数调用 -

bool flag = false;
int value = 0;

void writeflag () {
  flag = true;
}
void readflag () {
  while (!flag) {}
}
void th1 () {
  value = 1;
  writeflag(); // would function call prevent reordering?
}
void th2 () {
  readflag();  // would function call prevent reordering?
  assert (value == 1);    // would this fail???
}

Any idea?有什么想法吗?

A compiler barrier is not the same thing as a memory barrier.编译器屏障与内存屏障不同。 A compiler barrier prevents the compiler from moving code across the barrier.编译器屏障防止编译器跨屏障移动代码。 A memory barrier (loosely speaking) prevents the hardware from moving reads and writes across the barrier.内存屏障(松散地说)阻止硬件跨屏障移动读取和写入。 For atomics you need both, and you also need to ensure that values don't get torn when read or written.对于原子,您需要两者,并且您还需要确保值在读取或写入时不会被破坏。

Formally, no, if only because Link-Time Code Generation is a valid implementation choice and need not be optional.形式上,不,因为链接时代码生成是一个有效的实现选择,不需要是可选的。

There's also a second oversight, and that's escape analysis.还有第二个疏忽,那就是逃逸分析。 The claim is that "the compiler has no idea what the function's side effects will be."声称是“编译器不知道函数的副作用是什么。” , but if no pointers to my local variables escape from my function, then the compiler does know for sure that no other function changes them. ,但如果没有指向我的局部变量的指针从我的函数中逃脱,那么编译器确实知道没有其他函数更改它们。

In the second example, even if we assume that no reordering of any kind, the behavior is undefined.在第二个例子中,即使我们假设没有任何类型的重新排序,行为也是未定义的。

The writes and reads from variable flag are not atomic, and there is a race condition 1 .对变量 flag 的写入和读取不是原子的,并且存在竞争条件1 Having no reordering doesn't guarantee that both threads don't access the variable flat at the same time.没有重新排序并不能保证两个线程不会同时访问变量 flat。 This happens when one thread hits the while loop in the function readflag and reads flag, and the other thread writes to flag in writeflag.当一个线程命中函数 readflag 中的 while 循环并读取 flag,而另一个线程写入 writeflag 中的 flag 时,就会发生这种情况。


1 (Quoted from: ISO/IEC 14882:2011(E) 1.10 Multi-threaded executions and data races 21) 1 (引自:ISO/IEC 14882:2011(E) 1.10 多线程执行和数据竞争 21)
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other.如果一个程序在不同的线程中包含两个相互冲突的动作,那么它的执行就包含数据竞争,其中至少一个不是原子的,并且都不在另一个之前发生。 Any such data race results in undefined behavior任何此类数据竞争都会导致未定义的行为

You are confusing a memory barrier used for inter thread memory visibility and a compiler barrier, which isn't a thread device, just a device (or trick) to prevent reordering of side effects by the compiler .您混淆了用于线程间内存可见性的内存屏障和编译器屏障,后者不是线程设备,只是防止编译器重新排序副作用的设备(或技巧)。

You need a memory barrier for your threading example.您的线程示例需要一个内存屏障。

You can use a compiler barrier to ensure that memory side effet are performed in a given order (on the local CPU) for other purposes, like benchmarking, getting around a type aliasing violation, integrating assembly code, or signal handling (for a signal only handled in that same thread).您可以使用编译器屏障来确保以给定顺序(在本地 CPU 上)执行内存侧效应以用于其他目的,例如基准测试、绕过类型别名违规、集成汇编代码或信号处理(仅针对信号)在同一个线程中处理)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM