如何在中断到来时退出 sleep()?

How to exit sleep() when an interrupt arrives?

我正在寻找一种在用户中断到来时退出睡眠的方法。重要的是 退出 睡眠而不是这样做:中断睡眠,进行 ISR 处理,然后返回睡眠——这就是我正在寻找的解决方案。

我在 C++ 中寻找类似的东西 - C 中的等价物更好:

void *timer_thread(void *dummy)
{
  while(1)
  {
    // Check if some callbacks are to be given
    // when all are given, Determine x duration to sleep
  
    try
    {
      sleep(x);
    }
    except(/* the except block should hit ONLY when an interrupt arrives, 
              that too only when sleep() is executed. It's OK to delay 
              interrupt until the sleep is beginning execution */) 
    {
      //Do something else
    }
  }
}

到达的中断将主要告诉定时器线程应该减少睡眠以便更早地提供回调。但不管用例如何,我只需要在中断到来时以某种方式退出睡眠。我只是找不到有关如何执行此操作的信息。

PS:discard/NOP 如果在睡眠未发生时发生中断,则可以

这是在 Windows 10 (C/C++) 上的 Cygwin gcc v9.3.0 上。我不需要可移植的代码,因此任何特定于平台的解决方案都可以。

如果有其他类似于此工作的解决方案(不使用 sleep() 和中断),欢迎听取。我只是在寻找一种不涉及轮询的方式。

我不熟悉 windows 的线程库。所以我可以给出一个伪代码。您应该能够用 C 和 C++ 实现它。 (下面代码的语法明显错误)

创建一个单独的线程让sleep执行

void * mySleepThread(int x,mutex m)
{
  sleep(x);
  if(m.isNotDestroyed)
  {
     m.setValue(unlocked);
  }
  return;
}

你的主计时器线程看起来像:

void * timer_thread(void*dummy)
{
  while(1)
  {
    // Check if some callbacks are to be given
    // when all are given, Determine x duration to sleep


    create_mutex m1;
    m1.setValue(locked);

    //The Try sleep(x) block
    ExecuteThread(mySleepThread(x,m1));

    //The except block
    while(1)
    {
      if(m1.isNotDestroyed && m1.isLocked && interruptDetected)
        {
          m1.destroy;
          //Execute the interrupt statements here
          break;
        }

       else if(m1.isNotDestroyed && m1.isUnlocked)
       {
         m1.destroy;
         //Execute block of sleep finished without interrupt
         break;
       }
    }


    //Do something else block

  }
}

要在特定事件上等待特定时间 ,我会使用 std::mutex and std::condition_variable and specifically std::condition_variable::wait_for() 的组合来等待超时或某事的信号变化.

用于演示的最小示例程序:

#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
using namespace std::chrono_literals;

// a thread-shared flag to signal exit
std::atomic<bool> exitThread = false;

// a mutex to sync. inter-thread communication
std::mutex mtxAlert;
// a condition variable to signal changed data
std::condition_variable sigAlert;
// the data of inter-thread communication
bool alert = false;

void timerThread()
{
  // the timeout for timer thread
  auto timeout = 100ms;
  // runtime loop
  while (!exitThread) {
    // lock mutex (preparation to wait in cond. var.)
    std::unique_lock<std::mutex> lock(mtxAlert);
    // unlock mutex and wait for timeout or signaled alert
    sigAlert.wait_for(lock, timeout, []() { return alert || exitThread; });
    // mutex is locked again
    // check why wait_for() exited
    if (exitThread) {
      std::cout << "Timer thread exiting...\n";
      return;
    } else if (alert) {
      std::cout << "Timer was interrupted due to alert.\n";
      alert = false;
    } else {
      std::cout << "Timer achieved time-out.\n";
    }
  }
}

int main()
{
  std::thread threadWait(&timerThread);
  // wait a bit
  std::cout << "main(): Waiting 300ms...\n";
  std::this_thread::sleep_for(300ms);
  // sim. interrupt
  std::cout << "main(): Sim. interrupt.\n";
  { std::lock_guard<std::mutex> lock(mtxAlert);
    alert = true;
  }
  sigAlert.notify_all();
  // wait a bit
  std::cout << "main(): Waiting 50 ms...\n";
  std::this_thread::sleep_for(50ms);
  // sim. interrupt
  std::cout << "main(): Sim. interrupt.\n";
  { std::lock_guard<std::mutex> lock(mtxAlert);
    alert = true;
  }
  sigAlert.notify_all();
  // wait a bit
  std::cout << "main(): Waiting 50 ms...\n";
  std::this_thread::sleep_for(50ms);
  // exiting application
  exitThread = true;
  sigAlert.notify_all();
  threadWait.join();
  // done
  std::cout << "Done.\n";
}

输出:

main(): Waiting 300ms...
Timer achieved time-out.
Timer achieved time-out.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
Timer thread exiting...
Done.

Live Demo on coliru


OP 根据评论声称此示例未在 cygwin 上正确编译。我在我这边试过了,可以确认我修复的一些小问题:

  1. 缺少 #include <mutex> 添加

  2. std::atomic<bool> exitThread = false; 的初始化更改为

    std::atomic<bool> exitThread(false);
    

    我在使用 g++g++ -std=c++14 编译时得到了这个。 (好像-std=c++14是我默认的g++。)

    当我改用 g++ -std=c++17 时,我没有收到任何投诉。我坚信这与 copy-elision 有关,g++ 适用于 -std=c++17 但不是之前的。

但是,这是我在 cygwin64 的 Windows 10 笔记本电脑上使用稍微审查过的代码的示例会话:

$ g++ --version
g++ (GCC) 7.4.0

$
$ cat >testCondVar.cc <<'EOF'
> #include <atomic>
> #include <condition_variable>
> #include <iostream>
> #include <mutex>
> #include <thread>
> #include <chrono>
> using namespace std::chrono_literals;
> 
> // a thread-shared flag to signal exit
> std::atomic<bool> exitThread(false);
> 
> // a mutex to sync. inter-thread communication
> std::mutex mtxAlert;
> // a condition variable to signal changed data
> std::condition_variable sigAlert;
> // the data of inter-thread communication
> bool alert = false;
> 
> void timerThread()
> {
>   // the timeout for timer thread
>   auto timeout = 100ms;
>   // runtime loop
>   while (!exitThread) {
>     // lock mutex (preparation to wait in cond. var.)
>     std::unique_lock<std::mutex> lock(mtxAlert);
>     // unlock mutex and wait for timeout or signaled alert
>     sigAlert.wait_for(lock, timeout, []() { return alert || exitThread; });
>     // mutex is locked again
>     // check why wait_for() exited
>     if (exitThread) {
>       std::cout << "Timer thread exiting...\n";
>       return;
>     } else if (alert) {
>       std::cout << "Timer was interrupted due to alert.\n";
>       alert = false;
>     } else {
>       std::cout << "Timer achieved time-out.\n";
>     }
>   }
> }
> 
> int main()
> {
>   std::thread threadWait(&timerThread);
>   // wait a bit
>   std::cout << "main(): Waiting 300ms...\n";
>   std::this_thread::sleep_for(300ms);
>   // sim. interrupt
>   std::cout << "main(): Sim. interrupt.\n";
>   { std::lock_guard<std::mutex> lock(mtxAlert);
>     alert = true;
>   }
>   sigAlert.notify_all();
>   // wait a bit
>   std::cout << "main(): Waiting 50 ms...\n";
>   std::this_thread::sleep_for(50ms);
>   // sim. interrupt
>   std::cout << "main(): Sim. interrupt.\n";
>   { std::lock_guard<std::mutex> lock(mtxAlert);
>     alert = true;
>   }
>   sigAlert.notify_all();
>   // wait a bit
>   std::cout << "main(): Waiting 50 ms...\n";
>   std::this_thread::sleep_for(50ms);
>   // exiting application
>   exitThread = true;
>   sigAlert.notify_all();
>   threadWait.join();
>   // done
>   std::cout << "Done.\n";
> }
> EOF

$

编译并启动:

$ g++ -std=c++14 -o testCondVar testCondVar.cc

$ ./testCondVar
main(): Waiting 300ms...
Timer achieved time-out.
Timer achieved time-out.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
Timer thread exiting...
Done.

$

注:

此示例至少需要 C++14 的唯一原因是 std::chrono_literals 的使用,它允许使用例如300ms.

如果没有 std::chrono_literals,这可以写成 std::chrono::milliseconds(300)(诚然这不太方便)。分别替换所有 std::chrono_literals,我也能够用 -std=c++11 编译和 运行 样本。

我必须实现可以通过其他线程中断的 sleep,所以我想出了以下代码:

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

class InterruptibleTimer {
 public:
  InterruptibleTimer() : m_interrupted(false) {}

  template <class Rep, class Period>
  int sleep_for(const std::chrono::duration<Rep, Period>& delay) {

    /* Use wait_for timeout for sleeping. */
    std::unique_lock<std::mutex> lk(m_cv_mutex);
    if (m_cv.wait_for(lk, delay, [&, this]{return m_interrupted == true;})) {
      /* Timer was interrupted. */
      return 1;
    }

    /* Timer was finished. */
    return 2;
  }

  void interrupt() {
    m_cv.notify_all();
    {
      std::lock_guard<std::mutex> lk(m_cv_mutex);
      m_interrupted = true;
    }
    m_cv.notify_all();
    return;
  }

 private:
  std::condition_variable m_cv;
  std::mutex m_cv_mutex;
  std::atomic<bool> m_interrupted;
};

用法示例:

#include <thread>

void interrupt_after_2s(InterruptibleTimer& timer) {
  std::this_thread::sleep_for(std::chrono::seconds(2));
  timer.interrupt();
  return;
}

int main() {
  InterruptibleTimer timer;
  std::thread t1(interrupt_after_2s, std::ref(timer));
  timer.sleep_for(std::chrono::seconds(10));
  t1.join();
  return 0;
}

演示

$ clang++ -lpthread demo.cc -o demo
$ time ./demo
./demo  0.00s user 0.00s system 0% cpu 2.002 total