[英]C++ std::memory_order_relaxed and skip/stop flag
是否可以使用std::memory_order_relaxed
作为跳过标志,就像在iterate
:
constexpr static const std::size_t capacity = 128;
std::atomic<bool> aliveness[capacity];
T data[capacity]; // immutable/atomic/etc.
template<class Closure>
void iterate(Closure&& closure){
for(std::size_t i = 0; i<capacity; i++){
if (!aliveness[i].load(std::memory_order_relaxed)) continue;
closure( data[i] );
}
}
void erase(std::size_t index){
aliveness[index].store(false, std::memory_order_relaxed);
}
或者我应该使用发布/获取,而不是?
活着aliveness[i]
可能会再次变得“活着”。
iterate
并erase
多个线程中的调用。 在其他一些外部锁定下考虑data
不可变/原子/同步。
假设:当您使用erase
时,其他线程可以随时运行iterate()
。 (该问题的早期版本没有指定不可变。如果更新data[i]
的锁定(或缺少)没有被命令,那么这个答案仍然是相关的。写入alive[i]
。)
如果数据真的是不可变的,那么mo_relaxed
绝对没问题,除非您需要针对线程所做的其他事情对这些商店的全局可见性进行排序。 mo_relaxed
存储将始终最终对其他线程可见(并且在当前CPU上,将非常快速地这样做)。
如果您要在alive[i]
为false时修改非原子data[i]
则需要确保其他线程在修改时未使用其值。 这将是C ++中的UB,以及真实硬件上的实际正确性问题,具体取决于T
和closure
。
获取语义将适用于iterate
。 访问data[i]
在逻辑上后会发生alive[i]
因为阻挡单向处于正确的方向。 获取加载不会在以后的加载或存储中重新排序,只能在之前加载或存储 。
但是erase
存储是一个问题。 在对data[i]
进行任何修改之前,它需要全局可见data[i]
。 但是发布商店可以在以后的商店重新订购。 你需要的是一个释放围栏,以阻止两个方向的商店重新排序 。
void erase(std::size_t index){
aliveness[index].store(false, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
}
如果T
是原子类型,对data[i]
的释放存储就可以了。 但是不要这样做; 如果T
太大而无法锁定原子,那将会很糟糕。
在大多数实现中,seq-cst存储也可以工作,但我认为仅作为实现细节。 它通常会导致商店+全屏障asm指令。 (例如x86 MFENCE)。 所以它的工作原理只是因为编译器将seq-cst存储实现为store + thread_fence(seq_cst)
。
请注意,如果closure
修改data[]
,则iterate
不安全,除非一次只有一个线程可以调用它。 在哪种情况下,这是什么意思? 所以你可能应该使用
void iterate(Closure&& closure) const
{ ... }
所以iterate
仅适用于容器的const
对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.