理解memory_order_relaxed [英] Understanding memory_order_relaxed
问题描述
我想了解memory_order_relaxed的细节。我指的是以下链接: CPP参考。
#include< future>
使用
#include< atomic>
std :: atomic< int *> ptr {nullptr};
void fun1(){
ptr.store(new int {0},std :: memory_order_relaxed);
}
void fun2(){
while(!ptr.load(std :: memory_order_relaxed));
}
int main(){
std :: async(std :: launch :: async,fun1);
std :: async(std :: launch :: async,fun2);问题1:在上面的代码中,在技术上可能fun2是可能的在无限循环中,它看到ptr的值为nullptr,即使设置ptr的线程已经完成运行?
如果假设,我将上述代码更改为以下代码:
#include< future>
#include< atomic>
std :: atomic< int> i {0};
std :: atomic< int *> ptr {nullptr};
void fun1(){
i.store(1,std :: memory_order_relaxed);
i.store(2,std :: memory_order_relaxed);
ptr.store(new int {0},std :: memory_order_release);
}
void fun2(){
while(!ptr.load(std :: memory_order_acquire));
int x = i.load(std :: memory_order_relaxed);
}
int main(){
std :: async(std :: launch :: async,fun1);
std :: async(std :: launch :: async,fun2);
}
相关问题:在fun2上面的代码中,的原子i作为1或者它放心会看到值2?
解决方案一个有趣的观察是,代码,没有实际的并发;即
fun1
和fun2
顺序运行,原因是在特定条件下(包括调用使用
std :: launch :: async
启动策略),std :: future 由
std :: async
返回的code>对象具有析构函数块,直到启动的函数调用返回。由于忽略了返回对象,它的析构函数在语句结束之前被调用。如果你在main()(即在
之前启动
fun2
code>),你的程序将被捕获在一个无限循环中,因为fun1
永远不会运行。
此
std :: future
销毁等待行为有些争议(甚至在标准委员会中)因为我假设你不是这个意思,我将自由重写main
中的两个语句(两个示例)为:auto tmp1 = std :: async(std :: launch :: async,fun1);
auto tmp2 = std :: async(std :: launch :: async,fun2);
这将延迟实际的
std :: future
返回对象破坏直到main
结束,使fun1
和fun2
以异步方式运行。
在技术上可能fun2处于无限循环中,它看到ptr的值为nullptr,即使设置ptr已完成运行?
否,这不可能与
std :: atomic
(在一个真实的平台上,如评论部分所述)。使用非 -std :: atomic
变量,编译器可以(理论上)选择仅将值保存在寄存器中,但是std :: atomic
被存储,并且高速缓存一致性将该值传播到其他线程。使用std :: memory_order_relaxed
在这里很好,只要你不解引用指针。
在上面的代码中,fun2是否可以看到atom i的值为1或者是否可以看到值2?
保证在变量
x
中看到值2。
fun1
将两个不同的值存储到同一个变量,但由于存在明确的依赖关系,因此这些不会重新排序。
在
fun1
中,ptr.store
code> std :: memory_order_release阻止
i.store(2)
std :: memory_order_relaxed从下移到其发布屏障。在
fun2
中,ptr.load
与std :: memory_order_acquire
阻止i.load
与std :: memory_order_relaxed
跨越其获取障碍。这保证fun2
中的x
将具有值2.
请注意,通过在所有原子上使用
std :: memory_order_relaxed
,可以看到x $取决于对原子变量
i
的访问的相对顺序,对于ptr.store code>和
ptr.load
。I am trying to understand the specifics of memory_order_relaxed. I am referring to this link : CPP Reference.
#include <future> #include <atomic> std::atomic<int*> ptr {nullptr}; void fun1(){ ptr.store(new int{0}, std::memory_order_relaxed); } void fun2(){ while(!ptr.load(std::memory_order_relaxed)); } int main(){ std::async(std::launch::async, fun1); std::async(std::launch::async, fun2); }
Question 1: In the code above, is it technically possible for fun2 to be in an infinite loop where it sees the value of ptr as nullptr even if the thread that sets ptr has finished running?
If suppose, I change the code above to something like this instead:
#include <future> #include <atomic> std::atomic<int> i {0}; std::atomic<int*> ptr {nullptr}; void fun1(){ i.store(1, std::memory_order_relaxed); i.store(2, std::memory_order_relaxed); ptr.store(new int{0}, std::memory_order_release); } void fun2(){ while(!ptr.load(std::memory_order_acquire)); int x = i.load(std::memory_order_relaxed); } int main(){ std::async(std::launch::async, fun1); std::async(std::launch::async, fun2); }
Related Question: Is it possible in the code above for fun2 to see the value of atomic i as 1 or is it assured that it will see the value 2?
解决方案An interesting observation is that, with your code, there is no actual concurrency; i.e.
fun1
andfun2
run sequentially, the reason being that, under specific conditions (including callingstd::async
with thestd::launch::async
launch policy), thestd::future
object returned bystd::async
has its destructor block until the launched function call returns. Since you disregard the return object, its destructor is called before the end of the statement. Had you reversed the two statements inmain()
(i.e. launchfun2
beforefun1
), your program would have been caught in an infinite loop sincefun1
would never run.This
std::future
wait-upon-destruction behavior is somewhat controversial (even within the standards committee) and since I assume you didn't mean that, I will take the liberty to rewrite the 2 statements inmain
for (both examples) to:auto tmp1 = std::async(std::launch::async, fun1); auto tmp2 = std::async(std::launch::async, fun2);
This will defer the actual
std::future
return object destruction till the end ofmain
so thatfun1
andfun2
get to run asynchronously.is it technically possible for fun2 to be in an infinite loop where it sees the value of ptr as nullptr even if the thread that sets ptr has finished running?
No, this is not possible with
std::atomic
(on a real platform, as was mentioned in the comments section). With a non-std::atomic
variable, the compiler could (theoretically) have chosen to keep the value in register only, but astd::atomic
is stored and cache coherency will propagate the value to other threads. Usingstd::memory_order_relaxed
is fine here as long as you don't dereference the pointer.Is it possible in the code above for fun2 to see the value of atomic i as 1 or is it assured that it will see the value 2?
It is guaranteed to see value 2 in variable
x
.
fun1
stores two different values to the same variable, but since there is a clear dependency, these are not reordered.In
fun1
, theptr.store
withstd::memory_order_release
prevents thei.store(2)
withstd::memory_order_relaxed
from moving down below its release barrier. Infun2
, theptr.load
withstd::memory_order_acquire
prevents thei.load
withstd::memory_order_relaxed
from moving up across its acquire barrier. This guarantees thatx
infun2
will have value 2.Note that by using
std::memory_order_relaxed
on all atomics, it would be possible to seex
with value 0, 1 or 2, depending on the relative ordering of access to atomic variablei
with regards toptr.store
andptr.load
.这篇关于理解memory_order_relaxed的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!