std::memory_order_relaxed 足以检查"Availability"吗?

Is std::memory_order_relaxed enough for checking "Availability"?

本文关键字:Availability 检查 memory order relaxed std      更新时间:2023-10-16

我有一个并发对象,它可能或可能每次都持有指向函数的指针。对象的架构如下所示:

struct ConcurrentObject{
  //variables
  std::atomic<void(*)()> callback;
} 

因此,一个线程可能会决定要用这个对象附加回调并将其向前传递:

ConcurrentObject* co = new ConcurrentObject(); //I'm using smart pointers, no worries.
//do some logic
co->callback = someCallback; //void(*)() , this may be difference callback every time

修改后我得到了这个对象,并检查回调是否可用:

auto co = aquireConcurrentObject();
auto callback = co->callback.load();
if (callback){
    callback()
}

现在,我们知道,在不指定任何内存顺序的情况下,传递的默认内存顺序是memory_order_seq_cst它告诉编译器(简而言之)"不要乱写任何读取或写入指令以使程序更快,保持代码指定的指令的相对顺序,并通过 CPU 使其可见"。

我们也知道它是一个很好的性能顽固,因为编译器可以采取的操作受到更多限制。

我的问题是 - std::memory_order_relaxed足以采取这一行动吗?

是的,你是对的,在你的例子中 std::memory_order_relaxed 是安全的,因为你的代码只依赖于回调是原子的事实。您的代码不受内存操作可能重新排序的影响

回调指针访问的内存顺序会影响回调使用的变量的"可见性"。

如果您的回拨:

1)类似于constexpr,也就是说,除了参数和常量全局变量之外,它不使用任何东西,或者

2) 仅使用变量,这些变量在可能使用回调之前(发生之前)初始化,

然后使用 std::memory_order_relaxed 对于存储和加载都是可以的。

但是如果你的代码在 //do some logic 下初始化了一些变量,由回调使用,那么你至少应该使用 std::memory_order_release/std::memory_order_acquire 进行相应的存储和加载。否则,执行回调可能会看到这些变量未初始化(更确切地说,它将是来自 C++11 标准的数据竞赛,这是未定义的行为)。

您有办法衡量性能影响吗?更改内存顺序可能看起来像一个良好的性能启动(它似乎有效),但实际上,在大多数应用程序中 - 这并不重要。

在这里更改内存模型意味着任何维护此代码的人都需要格外小心,不要破坏它,因此这是您在执行此类操作时必须承担的另一个风险。

这种优化需要很好地记录下来,并在被证明是性能瓶颈之后进行选择。如果没有必要,请不要惹它。