无锁线程间的数据可见性

data visibility between threads without lock

我了解 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()中间读取一个"half-ready"状态也不是问题,我只是想确保我得到线程1写入的所有更改在明确定义的点之后。

基于this article它应该可以工作,但我在网上没有看到这样的解决方案,并且不容易测试它是否真的符合我的意图。

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

it is not a problem if thread 2 reads a "half-ready" state in the middle of 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 个对象符合条件)。

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