繁体   English   中英

C ++:仍然需要std :: atomic的__sync_synchronize()吗?

[英]C++: __sync_synchronize() still needed with std::atomic?

我一直遇到不常见但又经常发生的比赛情况。 该程序有两个线程,并使用std :: atomic。 我将代码的关键部分简化为:

std::atomic<uint64_t> b;  // flag, initialized to 0
uint64_t data[100];  // shared data, initialized to 0

线程1(发布):

// set various shared variables here, for example
data[5] = 10;

uint64_t a = b.exchange(1);  // signal to thread 2 that data is ready

线程2(接收):

if (b.load() != 0) {  // signal that data is ready
  // read various shared variables here, for example:
  uint64_t x = data[5];
  // race condition sometimes (x sometimes not consistent)
}

奇怪的是,当我向每个线程添加__sync_synchronize()时,竞争条件就会消失。 我已经看到这发生在两个不同的服务器上。

即当我将代码更改为如下所示时,问题就消失了:

线程1(发布):

// set various shared variables here, for example
data[5] = 10;

__sync_synchronize();
uint64_t a = b.exchange(1);  // signal to thread 2 that data is ready

线程2(接收):

if (b.load() != 0) {  // signal that data is ready
  __sync_synchronize();
  // read various shared variables here, for example:
  uint64_t x = data[5];
}

为什么需要__sync_synchronize()? 由于我认为交换和加载都可以确保逻辑的正确顺序,因此这似乎是多余的。

架构是x86_64处理器,Linux,G ++ 4.6.2

虽然无法通过简化的代码说出实际应用程序中实际发生了什么,但是__sync_synchronize帮助,并且该函数是内存障碍的事实告诉我,您在一个线程中编写东西,而另一个线程以一种非原子的方式阅读。

一个例子:

thread_1:

    object *p = new object;
    p->x = 1;
    b.exchange(p);   /* give pointer p to other thread */

thread_2:

    object *p = b.load();
    if (p->x == 1) do_stuff();
    else error("Huh?");

这很可能触发线程2中的错误路径,因为当线程2读取新的指针值p时, p->x的写入实际上尚未完成。

在这种情况下,在thread_1代码中添加内存屏障应该可以解决此问题。 请注意,在这种情况下,thread_2中的内存屏障将无法执行任何操作-它可能会更改时间并似乎可以解决问题,但这并不是正确的选择。 如果您正在读取/写入两个线程之间共享的内存,则可能仍然需要两侧都有内存屏障。

我了解这可能并不完全是您的代码正在执行的操作,但是概念是相同的__sync_synchronize确保在该函数调用之前,所有指令的内存读取和内存写入均已完成[这不是真正的函数调用,它将内联一条指令,等待任何未完成的内存操作完成]。

值得注意的是,对std::atomic操作仅会影响存储在atomic对象中的实际数据。 不读取/写入其他数据。

有时,您还需要一个“编译器屏障”,以避免编译器将内容从操作的一侧移到另一侧:

  std::atomic<bool> flag(false);
  value = 42;
  flag.store(true);

  ....

另一个线程:

  while(!flag.load());
  print(value); 

现在,编译器有可能生成第一种形式,如下所示:

  flag.store(true);
  value = 42;

现在,那不是很好,是吗? std :: atomic肯定会成为“编译器障碍”,但在其他情况下,编译器可能会以类似的方式很好地摆弄东西。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM