编译器优化在C ++中的作用

时间:2018-03-29 20:40:26

标签: c++ g++

我有以下代码在调试版本中工作,而不是在启用了g ++优化的版本构建中。 (当我说工作时,我的意思是当主线程将标志停止设置为true时,循环线程存在)。我知道可以通过添加volatile关键字来修复此问题。然而,我的问题是要了解在这种情况下究竟发生了什么。这是代码:

void main() {

   bool stop = false;

    std::thread reader_thread;

    auto reader = [&]() {
      std::cout << "starting reader" << std::endl;
      //BindToCPU(reader_thread, 0);

      while(!stop) {
      }

      std::cout << "exiting reader" << std::endl;
    };


    reader_thread = std::thread(reader);



     sleep(2);
     stop = true;
     std::cout << "stopped" << std::endl;

    reader_thread.join();
}

为什么会这样?是因为编译器优化吗?或者是因为缓存一致性问题?关于底下究竟发生了什么的一些细节值得赞赏。

3 个答案:

答案 0 :(得分:3)

程序的行为未定义。问题是两个线程都访问名为stop的变量,其中一个正在写入它。在C ++标准中,这是数据争用的定义,结果是未定义的行为。要摆脱数据竞争,您必须以某种形式引入同步。最简单的方法是将stop的类型从bool更改为std::atomic<bool>。或者,您可以使用互斥锁,但对于此特定示例,这将是过度杀伤。

stop标记为volatile可能会使症状消失,但不能解决问题

答案 1 :(得分:2)

问题在于编译器,特别是优化阶段无法判断程序实际上是做了什么。特别是,它不能说“停止”可以是除了假之外的任何东西。最好和最简单的解决方案是使“停止”原子。这是更正的程序,额外收费的“睡眠”程序。

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

inline void std_sleep(long double seconds) noexcept
{    
    using duration_t = std::chrono::duration<long long, std::nano>;
    const auto duration =  duration_t(static_cast<long long> (seconds * 1e9));
    std::this_thread::sleep_for(duration);
}


int main() {

   std::atomic<bool> stop = false;

    std::thread reader_thread;

    auto reader = [&stop]() {
      std::cout << "starting reader" << std::endl;
      //BindToCPU(reader_thread, 0);

      while(!stop) {
          std_sleep(.25);
      }

      std::cout << "exiting reader" << std::endl;
    };


    reader_thread = std::thread(reader);

    std_sleep(2.0);
     stop = true;
     std::cout << "stopped" << std::endl;

    reader_thread.join();
    return 0;
}

答案 2 :(得分:1)

您有多个线程访问同一个变量,其中一个写入变量。这种情况称为数据竞争。数据竞争是未定义的行为,编译器倾向于在这些情况下做有趣/灾难性的事情。

符合您描述的一个示例在here第2.3节中说明:

  

...违反了编译器的假设,即普通变量在没有被赋值的情况下不会改变...如果变量根本没有注释,    循环等待另一个线程设置标志:

   while (!flag) {}
  

甚至可以转换为现在可能无限,但顺序等效,循环

   tmp = flag; // tmp is local
   while (!tmp) {}

关于此类竞争条件的另一篇文章是this one