了解 memory_order_relaxed

MS *_*nth 6 c++ multithreading c++11 relaxed-atomics

我试图了解 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);
}
Run Code Online (Sandbox Code Playgroud)

问题1:在上面的代码中,即使设置ptr的线程已经完成运行,fun2在技术上是否可能处于无限循环中,它看到ptr的值为nullptr?

如果假设,我将上面的代码更改为如下所示:

#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);
}
Run Code Online (Sandbox Code Playgroud)

相关问题:在上面的代码中,fun2 是否有可能将原子 i 的值视为 1 或者是否可以保证它会看到值 2?

LWi*_*sey 7

一个有趣的观察是,对于您的代码,没有实际的并发性;即fun1并按fun2顺序运行,原因是在特定条件下(包括std::async使用std::launch::async启动策略调用),std::future返回的对象std::async具有其析构函数块,直到启动的函数调用返回。由于您忽略了返回对象,因此在语句结束之前调用其析构函数。如果您颠倒了main()(即fun2在之前启动fun1)中的两个语句,您的程序将陷入无限循环,因为fun1永远不会运行。

这种std::future等待销毁行为有些争议(即使在标准委员会内部),并且由于我认为您不是这个意思,我将冒昧地将mainfor(两个示例)中的 2 个语句重写为:

auto tmp1 = std::async(std::launch::async, fun1);
auto tmp2 = std::async(std::launch::async, fun2);
Run Code Online (Sandbox Code Playgroud)

这将推迟实际std::future返回的对象破坏,直到结束main,这样fun1fun2获得异步运行。

fun2 在技术上是否可能处于无限循环中,即使设置 ptr 的线程已完成运行,它也会将 ptr 的值视为 nullptr?

不,这是不可能的std::atomic(在真实平台上,如评论部分所述)。对于非std::atomic变量,编译器可以(理论上)选择仅将值保留在寄存器中,但std::atomic存储 a 并且缓存一致性会将值传播到其他线程。std::memory_order_relaxed只要您不取消引用指针,在这里使用就可以了。

在上面的代码中,fun2 是否有可能将原子 i 的值视为 1,或者是否可以保证它会看到值 2?

保证在变量中看到值 2 x
fun1将两个不同的值存储到同一个变量中,但由于存在明确的依赖关系,因此不会对它们进行重新排序。

在 中fun1ptr.storewithstd::memory_order_release防止i.store(2)withstd::memory_order_relaxed向下移动到其释放屏障以下。在 中fun2ptr.loadwithstd::memory_order_acquire防止i.loadwithstd::memory_order_relaxed向上移动越过它的获取障碍。这保证了xin 的fun2值为 2。

注意,通过使用std::memory_order_relaxed所有原子公司,这将是可以看到x具有值0,1或2,这取决于接入原子变量的相对顺序i与问候ptr.storeptr.load