了解memory_order_relaxed

时间:2015-07-01 00:40:45

标签: c++ multithreading c++11 relaxed-atomics

我试图了解memory_order_relaxed的细节。我指的是这个链接: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);
}

问题1:在上面的代码中,fun2在技术上是否可能处于无限循环中,即使设置ptr的线程已经完成运行,它也会将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);
}

相关问题:在上面的代码中,fun2是否有可能将原子i的值看作1,还是确保它会看到值2?

1 个答案:

答案 0 :(得分:3)

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

这种std::future等待破坏行为有点争议(即使在标准委员会内),因为我认为你并不意味着,我将冒昧地重写{中的2个陈述{1}}(两个例子):

main

这会将实际auto tmp1 = std::async(std::launch::async, fun1); auto tmp2 = std::async(std::launch::async, fun2); 返回对象销毁推迟到std::future结束,以便mainfun1异步运行。

  

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

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

  

在上面的代码中,fun2是否有可能将原子i的值看作1,还是确保它会看到值2?

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

fun1fun1 ptr.store std::memory_order_release阻止i.store(2) std::memory_order_relaxed向下移动到其释放障碍之下。在fun2ptr.load std::memory_order_acquire i.load阻止std::memory_order_relaxed x向上移动到其获取障碍。这可以保证fun2中的std::memory_order_relaxed具有值2.

请注意,通过在所有原子上使用x,可以看到i的值为0,1或2,具体取决于访问的相对顺序关于ptr.storeptr.load的原子变量Blob