编译器优化和线程

compiler optimisations and threads

我有一个在线程中执行任务的程序,我希望可以选择通过按键提前终止。这是一个 MWE:

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

int main(int argc, char *argv[]) {
  char endChar = 0;// endChar is only written by keyboard thread
  std::thread kbth([&endChar]() { std::cin >> endChar; });//this thread monitors the keyboard
  kbth.detach();

  std::chrono::seconds run_t{15};
  bool running = true; // running could be written by main and task thread at
                       // same time (unlikely)
  std::mutex task_mtx; // so protect with mutex
  std::thread task([&running, &run_t, &task_mtx]() {
    std::this_thread::sleep_for(run_t);
    std::scoped_lock lock{task_mtx};
    running = false;
  });                                                   //this thread performs the task
  task.detach();

  while (running) {                                     //running is false when the task ends
    if (endChar == 42) { // 42=*
      std::cout << "key pressed" << std::endl;
      std::scoped_lock lock{task_mtx};
      running = false;                                  //or when * is entered 
      break;
    }
  }
  std::cout << "bye" << std::endl;
}

这在没有编译器优化的情况下工作正常,但在优化时失败了。请有人能帮我理解为什么会这样吗?根据 this ,只要不改变“程序的可观察行为”,编译器就可以优化代码。因此,当优化开启时失败时,这不算改变可观察到的行为吗?我尝试使用 g++ 和 cl,总结:

// clang-format off
// g++ version 11.2.1 fedora 64bit
// g++ -O0 -std=c++1z ../src/kbthread.cpp -o kbthread -l pthread //OK, exits gracefully when done or when key pressed 
// g++ -O1 -std=c++1z ../src/kbthread.cpp -o kbthread -l pthread //fail, hang 
// g++ -O2 -std=c++1z ../src/kbthread.cpp -o kbthread -l pthread //fail, hang

// cl version 19.29.30133 for x64 windows
// cl /EHsc -std:c++17 -Od -MD ../src/kbthread.cpp //OK, exits gracefully when done or when key pressed 
// cl /EHsc -std:c++17 -O1 -MD ../src/kbthread.cpp // fail, immediately prints "key pressed\nbye" 
// cl /EHsc -std:c++17 -O2 -MD ../src/kbthread.cpp // fail, immediately prints "key pressed\nbye"
// clang-format on

好的,正如下面的评论指出的线程问题@François Andrieux 我已经用下面的代码使用原子“修复”了它:

int main(int argc, char *argv[]) {
  std::atomic_char endChar{0};
  std::thread kbth([&endChar]() { // this thread monitors the keyboard
    char buff = 0;
    std::cin >> buff;
    endChar = buff;
  });
  kbth.detach();

  std::chrono::seconds run_t{10};
  std::atomic_bool running{true};
  std::thread task([&running, &run_t]() { // this thread performs the task
    std::this_thread::sleep_for(run_t);
    running = false;
  });
  task.detach();

  while (running.load()) { // running is false when the task ends, so the program ends
    if (endChar.load() == 42) { // 42=*
      std::cout << "key pressed" << std::endl;
      running = false;     // or when * is entered, so the program ends early
      break;
    }
  }
  std::cout << "bye" << std::endl;
}

仍然不确定分离的使用,但现在似乎工作正常。