我有一个并发对象,可能或者可能每次都保存指向函数的指针。该对象的模式如下所示:
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
,它告诉编译器(简而言之)&#34;不要乱写任何读或写指令以使程序更快,保持代码规定的相对指令顺序,并通过cpu&#34;&#34;使其可见。
我们也知道这是一个很好的表现,因为编译器可以采取的行动受到更多限制。
我的问题是 - std::memory_order_relaxed
对此行动是否足够?
答案 0 :(得分:1)
是的,你是对的,在你的例子中,std :: memory_order_relaxed可以安全使用,因为你的代码只依赖于回调是原子的这一事实。您的代码不受可能的内存操作重新排序的影响
答案 1 :(得分:1)
回调指针访问的内存顺序会影响回调使用的变量的“可见性”。
如果你的回调:
1) constexpr -like,即它除了参数和常量全局变量之外不使用任何东西,或者
2)仅使用变量,初始化之前(发生在之前)可能使用回调,
然后使用std::memory_order_relaxed
可以存储和加载。
但如果//do some logic
下的代码初始化了回调使用的某些变量,那么您应该使用至少 std::memory_order_release
/ std::memory_order_acquire
进行存储和加载correspondigly。否则,执行回调可能会看到这些变量未初始化(更为严格的是,它将是数据竞争来自C ++ 11标准,即未定义行为)。
答案 2 :(得分:0)
您是否有办法衡量效果?更改内存顺序可能看起来像是一个良好的性能启动(似乎有效),但实际上,在大多数应用程序中 - 它都没关系。
在这里更改内存模型意味着任何维护此代码的人都需要格外小心,不要破坏它,这样做这样做会带来另一个风险。
这种优化需要非常好地记录并在被证明是性能瓶颈之后进行选择。如果你不需要,不要乱用它。