停止长睡眠线程

Pap*_*ter 24 c++ multithreading thread-sleep c++11

假设我有一个线程,应该定期执行一些任务,但这个时期是 每小时6次每小时12次(每5分钟),我经常看到用一个is_running标志控制线程循环的代码,每个循环检查一次,如下所示:

std::atomic<bool> is_running;

void start()
{
    is_running.store(true);
    std::thread { thread_function }.detach();
}

void stop()
{
    is_running.store(false);
}

void thread_function()
{
    using namespace std::literals;
    while (is_running.load())
    {
        // do some task...
        std::this_thread::sleep_for(5min);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果stop()函数被调用,比如说,start()在线程处于活动状态之后1毫秒,直到它唤醒299999毫秒,检查标志并死掉.

我的理解是否正确?如何避免保持活着(但是睡觉)应该已经结束的线程?到目前为止,我最好的方法如下:

void thread_function()
{
    using namespace std::literals;
    while (is_running.load())
    {
        // do some task...
        for (unsigned int b = 0u, e = 1500u; is_running.load() && (b != e); ++b)
        {
            // 1500 * 200 = 300000ms = 5min
            std::this_thread::sleep_for(200ms);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有更简单,更直接的方法来实现这一目标?

Yak*_*ont 22

使用条件变量.你等待条件变量 5分钟通过.记得检查虚假的唤醒.

cppreference

关于如何在一两分钟的谷歌搜索中使用条件变量,我找不到一个好的堆栈溢出帖子.棘手的部分是意识到wait可以通过5分钟通过而不是发送信号来唤醒.处理这个问题最干净的方法是使用带有lambda的wait方法,该方法会仔细检查唤醒是否为"好".

是cppreference上的一些示例代码,它wait_until与lambda一起使用.(wait_forlambda相当于wait_untillambda).我稍微修改了一下.

这是一个版本:

struct timer_killer {
  // returns false if killed:
  template<class R, class P>
  bool wait_for( std::chrono::duration<R,P> const& time ) const {
    std::unique_lock<std::mutex> lock(m);
    return !cv.wait_for(lock, time, [&]{return terminate;});
  }
  void kill() {
    std::unique_lock<std::mutex> lock(m);
    terminate=true; // should be modified inside mutex lock
    cv.notify_all(); // it is safe, and *sometimes* optimal, to do this outside the lock
  }
  // I like to explicitly delete/default special member functions:
  timer_killer() = default;
  timer_killer(timer_killer&&)=delete;
  timer_killer(timer_killer const&)=delete;
  timer_killer& operator=(timer_killer&&)=delete;
  timer_killer& operator=(timer_killer const&)=delete;
private:
  mutable std::condition_variable cv;
  mutable std::mutex m;
  bool terminate = false;
};
Run Code Online (Sandbox Code Playgroud)

实例.

timer_killer在共享点创建一个.客户端线程可以wait_for( time ).如果它返回false,则表示您在等待完成之前被杀的地方.

控制线程只是调用kill(),每个人都在做wait_for一个false返回.

请注意,存在一些争用(锁定互斥锁),因此这不适用于无限线程(但很少有东西).如果需要使用任意延迟运行的无限数量的任务而不是每个延迟重复任务的完整线程,请考虑使用调度程序 - 每个实际线程都超过一兆字节的系统地址空间(仅用于堆栈) .


Mar*_*k B 5

有两种传统方法可以做到这一点。

您可以对条件变量使用定时等待,并让另一个线程向您的周期性线程发出信号,以便在到了时间时唤醒并终止。

或者,你也可以poll在管道上用睡眠作为超时,而不是睡觉。然后,您只需向管道写入一个字节,线程就会醒来并退出。