我想使用c ++ 11原子库编写一个seqlock。我已阅读有关stackoverflow上seqlock的一些问题?但是没人帮我 我使用的算法很常见,您可以在任何地方找到它,这是我的代码:
struct sequence_spinlock_t {
void write_lock() {
lock.lock();
flags.fetch_add(1, memory_order_acquire); //A
}
void write_unlock() {
flags.fetch_add(1, memory_order_release); //B
lock.unlock();
}
void read_enter(uintptr_t *flag) {
for (;;) {
uintptr_t f = flags.load(memory_order_acquire); //C
if ((f & 1) == 0) {
*flag = f;
break;
}
pause();
}
}
bool_ read_leave(uintptr_t flag) {
uintptr_t f = flags.load(memory_order_relaxed); //D
return f == flag;
}
spinlock_t lock;
atomic_uintptr_t flags;
};
//read thread
uintptr_t flag;
do {
lock.read_enter(&flag); (0)
//read something (1)
} while(!lock.read_leave(flag)) (2)
//write thread
lock.write_lock(); (3)
//write something (4)
lock.write_unlock(); (5)
Run Code Online (Sandbox Code Playgroud)
我确保我在B和C处正确使用了memory_order标记。
我认为这在A和D都是不正确的。
想想我们同时读取和写入受保护的数据,我担心D处的标志的读取值太旧了,我们不读取write_lock()写入的最新值,但是我们读取受保护的数据的最新值由写线程编写(在x86系统上可能不会发生,但是我不认为代码在x86上运行。)在读取线程完成读取受保护的数据之后,由于标志的读取值太旧,找不到序列已增加。从循环读取线程产量,我们犯了一个错误。
(1)中受保护数据的读取值写入(4),(2)中标志的读取值未写入(3)(这是我们上次解锁写锁时写入的值)。为什么我认为有错误。
但是我真的不知道要解决此问题。我试图在read_leavee()和write_locke()之间建立“同步”关系(我希望“ read_leave()与write_locke()同步”)。但是没有存储read_leave()中的操作,所以我失败了。
(哦!我很难理解c ++标准规范,部分原因是我不是来自英语国家。)
在 read_leave 中使用 memory_order_relaxed 本身是可以的,但您确实需要确保在加载标志变量之前已加载数据值。您可以使用 std::atomic_thread_fence 来完成此操作。即你的 read_leave 应该看起来像
布尔 read_leave(uintptr_t 标志){ atomic_thread_fence(内存_顺序_获取); uintptr_t f = flag.load(memory_order_relaxed); 返回 f == 标志; }
FWIW,通过此更改,您的代码看起来大致类似于http://safari.ece.cmu.edu/MSPC2012/slides_posters/boehm-slides.pdf中的示例 3
| 归档时间: |
|
| 查看次数: |
696 次 |
| 最近记录: |