【问题标题】:Interruptible sleep in std::threadstd::thread 中的可中断睡眠
【发布时间】:2020-05-02 12:05:55
【问题描述】:

我有一个简单的 C++11 线程程序,如下所示。

代码:

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

int main(int argc, char *argv[]) {

   std::cout << "My program starts" << std::endl;
   std::atomic<bool> exit_thread(false);
   std::thread my_thread = std::thread([&exit_thread]{
        do {
            std::cout << "Thread is doing something..." << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(5));
        } while (!exit_thread);
    });

   std::this_thread::sleep_for(std::chrono::seconds(12));
   exit_thread = true;
   std::cout << "Might have to wait to exit thread" << std::endl;
   my_thread.join();
   return 0;
}

正如你在上面看到的,有一个循环,它有一个sleep_for,它使线程休眠 5 秒,然后它唤醒并再次循环,前提是exit_thread 设置为 false。主线程等待 12 秒并准备退出,首先将 exit_thread 设置为 true,然后在线程上执行 join。到目前为止一切顺利。

问题:
以上是可以的,适用于客观。 但是存在“潜在问题”。如果线程现在刚刚开始休眠,那么它需要多花 4 秒才能从休眠中发现它现在需要退出。这会延迟退出过程和销毁。

问题:
如何使线程以可中断的方式休眠?这样我就可以通过取消睡眠而不是等待潜在的 4 或 3 或 2 秒来中断睡眠并让线程立即退出。

我认为使用std::condition_variable 可以解决这个问题?大概?我正在寻找一段代码来展示如何。

注意我的代码在 clang 和 gcc 上运行。

【问题讨论】:

  • 是的,您需要等待信号量或条件变量超时,而不是无条件休眠。

标签: multithreading c++11 sleep condition-variable stdthread


【解决方案1】:

我们应该等待条件变量或信号量而不是休眠。这是实现这一点的最小更改:

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

int main()
{
   std::cout << "My program starts" << std::endl;
   std::atomic<bool> exit_thread(false);
   std::condition_variable cv;
   std::mutex m;

   std::thread my_thread = std::thread([&exit_thread,&cv,&m]{
        do {
            std::cout << "Thread is doing something..." << std::endl;
            {
                std::unique_lock<std::mutex> lock(m);
                cv.wait_for(lock, std::chrono::seconds(5));
            }
        } while (!exit_thread);
    });

   std::this_thread::sleep_for(std::chrono::seconds(12));
   {
       std::lock_guard<std::mutex> guard(m);
       exit_thread = true;
   }
   cv.notify_all();

   std::cout << "Thread stops immediately" << std::endl;
   my_thread.join();
}

显然,我们确实需要互斥锁:

即使共享变量是原子的,也必须在 mutex 以便正确地将修改发布到等待 线程。

【讨论】:

  • 太棒了!非常感谢。在这个解决方案中,std::condition_variables 的虚假唤醒问题不是需要考虑的问题吗?在接受这个答案之前只需要这个确认。
  • 非常感谢关于原子变量的提示。如果我使用load()store(value) 技术,那么我不需要互斥体吗?可能这是一个不同的 SO 讨论的问题。
  • 你是否需要处理虚假唤醒是你必须决定的——如果你真的不能在 5 秒之前开始循环的下一次迭代,那么你将不得不检查它,否则没有。我认为您仍然需要具有原子load()/store() 的互斥锁,因为cv.wait_for 将锁作为其第一个参数 - 但请提出另一个问题以获得更明智的答案!我不是这里的专家。
猜你喜欢
  • 2015-11-20
  • 1970-01-01
  • 1970-01-01
  • 2015-01-28
  • 2014-07-07
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
相关资源
最近更新 更多