【问题标题】:How to exit sleep() when an interrupt arrives?中断到达时如何退出 sleep()?
【发布时间】:2020-08-13 02:48:44
【问题描述】:

我正在寻找一种在用户中断到达时退出睡眠的方法。重要的是退出睡眠而不是这样做:中断睡眠,执行 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:如果在没有发生睡眠时发生中断,可以丢弃/NOP 中断

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

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

【问题讨论】:

  • 这听起来有点绝望。使用睡眠,您可以告诉操作系统调度程序将您的线程挂起一段时间。我不相信有后门。要将延迟与其他条件混合,请考虑 std::mutexstd::condition_variable。不确定,这如何与您提到的中断服务例程一起使用。
  • 如果上面还有其他解决方案,我很乐意听到。不需要是 sleep() 函数,也不需要是中断。
  • 我不知道它在 Cygwin 中是否可用,但在 Linux 中一种可能的解决方案是(ab)使用epoll_wait()。这将等待某些事件在一组文件描述符上发生,或者直到发生超时。所以原则上你可以使用 epoll 超时来替换sleep(),但是通过从其他地方写入文件描述符来中止它。如果 Cygwin 中不存在 epoll,那么也许你可以使用类似的东西

标签: c++ c multithreading signals interrupt


【解决方案1】:

我必须实现可以被其他线程中断的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

【讨论】:

    【解决方案2】:

    要在某个事件上等待某个时间,我会使用std::mutexstd::condition_variable 的组合,特别是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 &lt;mutex&gt;

    2. std::atomic&lt;bool&gt; 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,但不是之前的。

    但是,这是我在cygwin64cygwin64 上使用 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 编译和运行示例。

    【讨论】:

    • 此代码在我的工作区中出现构建错误。它有任何先决条件吗?第 4 行:..\Offtarget Definition\src\ot_timer.cpp:31:32:错误:使用已删除的函数 'std::atomic::atomic(const std::atomic&)' 第 14 行: ..\Offtarget Definition\src\ot_timer.cpp:34:6: 错误:命名空间“std”中的“mutex”未命名类型第 21 行:..\Offtarget Definition\src\ot_timer.cpp:36:6 :错误:命名空间'std'中的'condition_variable'没有命名类型第49行:..\Offtarget Definition\src\ot_timer.cpp:255:27:错误:'mutex'不是'std'的成员.. . 仅举几例
    • @Arun std::mutex 需要 #include &lt;mutex&gt;。在我的示例中,它可能被间接包含在内。 (正如您在链接中看到的,它在 Linux 上使用 g++ 编译和运行良好。coliru 上的 g++ 版本目前是 9.2。)明天,我尝试在我的机器上的 cygwin 中编译并运行它。
    • @Arun 回答已审核,问题已修复。
    • 感谢@Scheff,我在再次编译之前添加了#include ,但又看到了问题,因此被问到。 C++14 实际上解决了这个问题——我实际上没有设置默认值。我会用不同的用例对此进行更彻底的测试,然后会回来
    • 知道为什么会发生这个错误吗?: ..\Offtarget Definition\src\ot_timer.cpp:291:8: error: 'std::this_thread' has not been declared
    【解决方案3】:

    我不熟悉 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
    
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      • 1970-01-01
      • 1970-01-01
      • 2019-07-20
      • 1970-01-01
      • 2018-10-23
      • 1970-01-01
      相关资源
      最近更新 更多