Custom function interrupt

前端 未结 2 955
不思量自难忘°
不思量自难忘° 2021-01-29 00:58

Is it possible to implement function interrupt in Qt (5.x).

For example if I have a button and want something to execute on the thread (which is running infinite loop) w

相关标签:
2条回答
  • 2021-01-29 01:05

    The infinite loop should be an event loop, and then it can automatically process cross-thread slot calls without you worrying about the details.

    The idiom to run code "continuously" on an event loop is the zero-duration timer.

    Let's say you start with code that looks like this:

    class MyThread : public QThread {
      bool button_is_clicked_flag = false;
      void run() override {
        forever{
          if (button_is_clicked_flag) {
            onButtonClick();
            button_is_clicked_flag = false;
          }
          doWork();
        }
      }
      void onButtonClick();
      void doWork();
    public:
      using QThread::QThread;
      void setButtonClickedFlag();
    }
    
    int main(int argc, char **argv) {
      ...
      MyThread t;
      t.start();
      ...
    }
    

    It is required for doWork() not to take too long - nor too short. If it took ~5ms on modern hardware, it'd be just about a right tradeoff between overhead and latency for a general-purpose application. If you need lower latency reaction in the worker thread, then doWork() must do less work. It probably doesn't make much sense for doWork() to take much less than 1ms.

    And whenever doWork() doesn't have anything to do, e.g. if it's done with the computation it was supposed to perform, it should stop the timer that keeps it alive.

    You should transform it to look as follows:

    class MyWorker : public QObject {
      Q_OBJECT
      QBasicTimer m_timer;
      void doWork();
      void timerEvent(QTimerEvent *event) {
        if (event->timerId() == m_timer.timerId())
          doWork();
      }
    public:
      explicit MyWorker(QObject *parent = nullptr) : QObject(parent) {
        m_timer.start(0, this);
      }
      Q_SLOT void onButtonClick() {
        // Ensure we're invoked correctly
        Q_ASSERT(QThread::currentThread() == thread());
        ...
      }
    }
    
    class Window : public QWidget {
      Ui::Window ui;
    public:
      Q_SIGNAL void buttonClicked();
      explicit Window(QWidget *parent = nullptr) : QWidget(parent) {
        ui.setupUi(this);
        connect(ui.button, &QPushButton::clicked, this, &Window::buttonClicked);
      }
    };
    
    class SafeThread : public QThread {
      Q_OBJECT
      using QThread::run; // final method
    public:
      ~SafeThread() { quit(); wait(); } // we're safe to destroy - always
    };
    
    int main(int argc, char **argv) {
      ...
      MyWorker worker;
      SafeThread thread;
      Window window;
      // onButtonClick will be executed in worker->thread()
      connect(&window, &Window::buttonClicked, &worker, &MyWorker::onButtonClick);
      worker.moveToThread(&thread);
      thread.start();
      window.show();
      return app.exec();
    }
    

    The event loop that runs in QThread::run will continuously invoke doWork via the timer event handler. But whenever a cross-thread slot call needs to be made to an object living in that thread, the event loop will deliver the internal QMetaCallEvent representing the slot call to QObject::event, which will then execute the call.

    Thus, when you set a breakpoint in onButtonClick, there will be QObject::event nearby on the call stack.

    0 讨论(0)
  • 2021-01-29 01:29

    You could start a thread and then immediately wait on a std::condition_variable, then when the button is clicked (the event being called on the main thread), notify the condition variable and the thread would awake.

    However, this is a bit strange. What are you trying to do? call an asynchronous task upon a button click? In that case, perhaps it would be better just to start one from the button click event with std::packaged_task or std::async.

    0 讨论(0)
提交回复
热议问题