线程之间的数据可见性,无需锁定



我了解 C++11 中内存排序的基本规则,尤其是发布-获取排序。我在两个线程之间共享了一大块内存,我不需要原子性,但希望确保最终一个线程所做的所有更改在另一个线程中可见,尤其是在具有宽松内存模型的平台上。

是否可以仅使用原子防护变量来触发内存同步? 例如,

std::atomic<bool> guardVar;
char *shared_mem=get_shared_mem();
(thread 1)
while(true) {
happens_many_things();
do_whatever_I_want_with_shared_mem();
guardVar.store(0, std::memory_order_release);
}
(in thread 2)
while(true) {
guardVar.load(std::memory_order_acquire);
read_shared_mem_no_problem_if_inconsistent();
}

同样,如果线程 2 在 do_whatever_I_want_with_shared_mem() 中间读取"半就绪"状态,这不是问题,我只想确保在明确定义的点之后获得线程 1 写入的所有更改。

根据这篇文章,它应该可以工作,但我在网上没有看到这样的解决方案,也不容易测试它是否真的符合我的意图。

可以吗?如果是,还有没有更优雅的方式?

如果线程 2 在 do_whatever_I_want_with_shared_mem() 中间读取"半就绪"状态,这不是问题

这是一个错误,如果其中一个线程正在修改数据,则无法通过多个线程访问共享内存。C++标准称之为数据竞赛,它会导致未定义的行为。

两个线程之间的访问需要同步,但使用std::atomic的方式不正确。线程 1 中的store_release紧随其后,再次访问相同的数据。load_acquire也一样;两个操作之间没有同步,因此您正在处理数据争用。

为了确保共享内存一次只能由一个线程访问,guardVar技术上可以像这样使用:

std::atomic<bool> guardVar{false};
(thread 1)
while(true) {
while (guardVar.exchange(true, std::memory_order_acquire));  // LOCK
happens_many_things();
do_whatever_I_want_with_shared_mem();
guardVar.store(false, std::memory_order_release);  // UNLOCK
}
(in thread 2)
while(true) {
while (guardVar.exchange(true, std::memory_order_acquire)); // LOCK
read_shared_mem_no_problem_if_inconsistent();
guardVar.store(false, std::memory_order_release);  // UNLOCK
}

但是由于这是以一种相当低效的方式将std::atomic用作互斥锁(请注意旋转),因此您确实应该使用std::mutex

更新:

仍然可以在不锁定的情况下使用共享内存,但是您有责任确保在共享内存中访问的每个对象都没有数据争用(std::atomic对象符合条件)。

然后,您或多或少会得到您在问题中描述的行为,其中第二个线程可能会看到"半就绪"状态(某些对象已更新,其他对象未更新)。如果没有同步,第二个线程无法真正知道第一个线程的更新何时完成,但至少同时读取/写入数据竞争自由对象是安全的。

最新更新