我正在运行一个线程,直到设置了一个标志为止。
std::atomic<bool> stop(false);
void f() {
while(!stop.load(std::memory_order_{relaxed,acquire})) {
do_the_job();
}
}
我想知道编译器是否可以像这样展开循环(但我不希望它发生)。
void f() {
while(!stop.load(std::memory_order_{relaxed,acquire})) {
do_the_job();
do_the_job();
do_the_job();
do_the_job();
... // unroll as many as the compiler wants
}
}
据说,易变性和原子性是正交的,但我有点困惑。编译器是否可以缓存原子变量的值并展开循环?如果编译器可以展开循环,那么我认为我必须将volatile
放在标志上,并且我想确定一下。我应该放置
volatile
吗?对于我的曖昧表示抱歉。我(猜测)理解了重新排序和
memory_order_*
的含义,并确信我完全理解了volatile
的含义。我认为
while()
循环可以转换为无限个这样的if
语句。void f() {
if(stop.load(std::memory_order_{relaxed,acquire})) return;
do_the_job();
if(stop.load(std::memory_order_{relaxed,acquire})) return;
do_the_job();
if(stop.load(std::memory_order_{relaxed,acquire})) return;
do_the_job();
...
}
鉴于给定的内存顺序并没有阻止先前顺序操作被移动到原子加载之后,如果没有使用volatile关键字,我认为可以重新排列。
void f() {
if(stop.load(std::memory_order_{relaxed,acquire})) return;
if(stop.load(std::memory_order_{relaxed,acquire})) return;
if(stop.load(std::memory_order_{relaxed,acquire})) return;
...
do_the_job();
do_the_job();
do_the_job();
...
}
如果原子操作不意味着易失性,那么我认为,在最坏的情况下,代码甚至可以像这样转换。void f() {
if(stop.load(std::memory_order_{relaxed,acquire})) return;
while(true) {
do_the_job();
}
}
永远不会有这样疯狂的实现,但我想这仍然是可能的情况。我认为防止这种情况的唯一方法是将 volatile
放到原子变量中,我正在询问此事。
我提出了很多猜测,请告诉我其中是否有任何错误。
std::atomic
的内容,但没有人说它应该是这样的。我猜,在类里面有一个volatile
变量。 - Nickvolatile
,因为它没有保证的多线程语义?如果是后者,为什么不提及你的平台呢? - David Schwartzstd::atomic
不需要在内部使用volatile
,因为volatile
对于线程间正确同步既非必要也不充分。使用volatile
将没有任何帮助。std::atomic
使用原子操作,而不是volatile
,因为它需要是原子的,不是易变的。它们是正交概念。http://isvolatileusefulwiththreads.com - Jonathan Wakely