原子获取是否与互斥锁释放同步?
Does an atomic acquire synchronize with mutex lock release?
我有一个对象将一些设置存储在 unordered_map
中,其中包含字符串键和变体值。由于我的库可能被多个线程使用,并且读取次数很可能大大超过写入次数,所以我考虑过写时复制实现,其中 "get" 操作是无锁的,而 "put" 操作处于关键部分,如示例所示:
class Cfg {
using M = unordered_map<string,X>;
shared_ptr<const M> data;
mutex write_lock;
public:
X get(string key) {
shared_ptr<const M> cur_ver = atomic_load_explicit(&data, memory_order_acquire);
// Extract the value from the immutable *cur_ver
}
void put(string key, X value) {
lock<muted> wlock(write_lock);
// No need for the atomic load here because of the lock
shared_ptr<const M> cur_ver = data;
shared_ptr<const M> new_ver = ;// create new map with value included
// QUESTION: do I need this store to be atomic? Is it even enough?
atomic_store_explicit(&data, new_ver, memory_order_release);
}
}
只要 acquire/release 同步还影响指向的数据而不仅仅是指针值,我有理由相信该设计有效。但是,我的问题如下:
- 锁内的原子存储是否需要此功能才能工作?
- 或者原子获取是否会与 "release" 操作的互斥解锁同步?
如果您希望 get
函数始终 return 最新值,则需要它。您可能会在同一时钟时间内进行多次读取和写入。使用原子内存顺序确保写入先于读取的顺序。
如果混合使用非原子存储和原子加载,这是未定义的行为。这个thread也讨论过了。您可能会一个接一个地写。如果你使用非原子指令,你可以有数据竞争。
memory_order_acquire
The operation is ordered to happen once all accesses to memory in the releasing thread (that have visible side effects on the loading thread) have happened.
memory_order_release
The operation is ordered to happen before a consume or acquire operation, serving as a synchronization point for other accesses to memory that may have visible side effects on the loading thread.
will the atomic acquire synchronize with the mutex unlock which is a "release" operation?
不,为了使获取操作与释放操作同步,获取操作必须观察释放操作的变化(或潜在的一些变化以该操作为首的释放序列)。
所以是的,您需要锁内的原子存储。无法保证 get
将 "see" 来自 put
的最新值,因为您只使用 acquire/release,因此存储和加载操作之间没有全序。如果你想要保证你必须使用 memory_order_seq_cst
.
作为旁注 - 此实现很可能不是无锁的,因为在大多数库实现中 atomic_load_explicit
for shared_ptr
不是无锁的。问题是您必须加载指针并取消引用该指针以增加引用计数器,在一个原子操作中。这在大多数架构上是不可能的,所以 atomic_load_explicit
通常使用锁来实现。
我有一个对象将一些设置存储在 unordered_map
中,其中包含字符串键和变体值。由于我的库可能被多个线程使用,并且读取次数很可能大大超过写入次数,所以我考虑过写时复制实现,其中 "get" 操作是无锁的,而 "put" 操作处于关键部分,如示例所示:
class Cfg {
using M = unordered_map<string,X>;
shared_ptr<const M> data;
mutex write_lock;
public:
X get(string key) {
shared_ptr<const M> cur_ver = atomic_load_explicit(&data, memory_order_acquire);
// Extract the value from the immutable *cur_ver
}
void put(string key, X value) {
lock<muted> wlock(write_lock);
// No need for the atomic load here because of the lock
shared_ptr<const M> cur_ver = data;
shared_ptr<const M> new_ver = ;// create new map with value included
// QUESTION: do I need this store to be atomic? Is it even enough?
atomic_store_explicit(&data, new_ver, memory_order_release);
}
}
只要 acquire/release 同步还影响指向的数据而不仅仅是指针值,我有理由相信该设计有效。但是,我的问题如下:
- 锁内的原子存储是否需要此功能才能工作?
- 或者原子获取是否会与 "release" 操作的互斥解锁同步?
如果您希望 get
函数始终 return 最新值,则需要它。您可能会在同一时钟时间内进行多次读取和写入。使用原子内存顺序确保写入先于读取的顺序。
如果混合使用非原子存储和原子加载,这是未定义的行为。这个thread也讨论过了。您可能会一个接一个地写。如果你使用非原子指令,你可以有数据竞争。
memory_order_acquire
The operation is ordered to happen once all accesses to memory in the releasing thread (that have visible side effects on the loading thread) have happened.
memory_order_release
The operation is ordered to happen before a consume or acquire operation, serving as a synchronization point for other accesses to memory that may have visible side effects on the loading thread.
will the atomic acquire synchronize with the mutex unlock which is a "release" operation?
不,为了使获取操作与释放操作同步,获取操作必须观察释放操作的变化(或潜在的一些变化以该操作为首的释放序列)。
所以是的,您需要锁内的原子存储。无法保证 get
将 "see" 来自 put
的最新值,因为您只使用 acquire/release,因此存储和加载操作之间没有全序。如果你想要保证你必须使用 memory_order_seq_cst
.
作为旁注 - 此实现很可能不是无锁的,因为在大多数库实现中 atomic_load_explicit
for shared_ptr
不是无锁的。问题是您必须加载指针并取消引用该指针以增加引用计数器,在一个原子操作中。这在大多数架构上是不可能的,所以 atomic_load_explicit
通常使用锁来实现。