9

我试图了解 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 的线程已经完成运行,它也会将 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 是否有可能将 atomic i 的值视为 1 或者确定它会看到值 2?

4

1 回答 1

15

一个有趣的观察是,对于您的代码,没有实际的并发性。iefun1并按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);

这会将实际的std::future返回对象销毁推迟到结束,以便main异步运行。 fun1fun2

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

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

在上面的代码中 fun2 是否可以将 atomic i 的值视为 1 或者确定它会看到值 2?

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

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

请注意,通过std::memory_order_relaxed所有原子上使用,可能会看到x值 0、1 或 2,这取决于访问原子变量i关于ptr.store和的相对顺序ptr.load

于 2016-07-31T00:30:13.460 回答