与C ++原子内存围墙同步

时间:2019-10-02 20:19:25

标签: c++ multithreading atomic memory-barriers stdatomic

我对以下使用内存围栏的代码同步有疑问。

std::atomic<int> a = 0;
std::atomic<int> b = 0;

void increase_b() {
  std::atomic_thread_fence(std::memory_order_release);
  b.store(1, std::memory_ordered_relaxed);
}

bool diff() {
  int val_a = a.load(std::memory_ordered_relaxed);
  int val_b = b.load(std::memory_ordered_relaxed);
  return val_b > val_a;
}

void f1() {
  increase_b();
  std::atomic_thread_fence(std::memory_order_seq_cst);
}

void f2() {
  std::atomic_thread_fence(std::memory_order_seq_cst);
  bool result = diff();
}

int main() {
  std::thread t1(f1);
  std::thread t2(f2);
  t1.join(); t2.join();
}

假设t1已完成f1,然后t2刚开始f2,将t2 看到b递增了?

1 个答案:

答案 0 :(得分:2)

您的代码过于复杂。 a=0永不更改,因此始终始终读为0。您可能只拥有atomic<int> b=0;,并且只有一个负载返回了b.load

  

假设t1完成f1,然后t2刚开始f2,t2会看到b增加了吗?

除非您将t1.join()的{​​{1}} 放在std::thread t2(f2);的前面,否则您无法检测到计时是如何完成的。这将要求线程2中的所有内容都在线程1中的所有内容之后进行排序。(我认为即使在f1的末尾没有seq_cst栅栏,但这也无济于事。我认为thread.join可以确保一切完成thread.join之后,线程内部可见)

但是,是的,排序可以偶然发生,然后当然可以。

使用C ++术语无法保证甚至是有意义的条件。

但是可以肯定的是,对于大多数(所有?)真正的实现,这都是可能发生的。 thread_fence(mo_seq_cst)将编译为一个完全屏障,该屏障会阻塞该线程,直到存储提交为止(成为所有线程全局可见)。因此执行不会离开f1,直到从其他线程读取可以看到b的更新值。 (C ++标准在创建同步关系时定义了顺序和隔离,而不是在编译为刷新存储缓冲区的全部障碍方面进行了定义。该标准未提及存储缓冲区或StoreLoad重新排序或任何CPU内存-点东西。)

鉴于综合条件,实际上实际上是按顺序对线程进行排序。彼此之间,并且就像一切都在单个线程中完成一样。


diff()中的装载没有按顺序排序。彼此都是因为它们都是mo_relaxed。但是a永远不会被任何线程修改,因此唯一的问题是b.load()是否可以在线程甚至启动之前,在f1存储可见之前发生。在实际的实现中,不能因为“ 然后 t2刚开始f2”的意思。 如果它可以加载旧值,那么您将无法说出“ 然后”,因此这几乎是一种重言式。

加载前的thread_fence(seq_cst)并没有任何帮助。我猜想它会阻止b.load()使用线程启动机制重新排序。