将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]
可能变成&#34;活着&#34;试。
iterate
和erase
同时从多个线程调用。在其他一些外部锁定下考虑data
immutable / atomic / synchronized。
答案 0 :(得分:4)
假设:当您使用iterate()
时,其他线程可以随时erase
运行。 (问题的早期版本没有指定不可变。如果更新data[i]
的锁定(或缺少)不是有序的,那么这个答案仍然是相关的。写入alive[i]
。)
如果数据真的是不可变的,那么mo_relaxed
绝对没问题,除非您需要针对线程正在做的事情 else 对这些商店的全局可见性进行排序。 mo_relaxed
存储将始终最终对其他线程可见(并且在当前的CPU上,将非常快速地显示)。
如果您在data[i]
为false时修改非原子alive[i]
,则需要确保其他线程在使用其值时不会使用其值{#1}}。被修改。那将是C ++中的UB,以及真实硬件上的实际正确性问题,具体取决于T
和closure
。
获取语义适用于iterate
。在data[i]
之后逻辑上可以访问alive[i]
,因为单向屏障的方向正确。 The acquire-load won't reorder with later loads or stores, only earlier ones
但是erase
中的商店是个问题。在对data[i]
进行任何修改之前,它必须是全局可见的。但是发布商店可以在以后的商店重新订购。你需要的是a release fence to block reordering of stores in both directions。
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存储实现为存储+ thread_fence(seq_cst)
。
请注意iterate
如果closure
修改data[]
,则void iterate(Closure&& closure) const
{ ... }
不安全,除非一次只有一个线程可以调用它。在这种情况下,这是什么意思?所以你应该使用
iterate
因此const
仅适用于容器的Error: Hostname/IP doesn't match certificate's altnames: "Host: [s3bucket].s3.amazonaws.com. is not in the cert's altnames: DNS:[mydomain].com, DNS:www.[mydomain].com"
个对象。