自定义功能中断

时间:2016-12-04 15:13:06

标签: c++ qt c++11 qt5

是否可以在Qt(5.x)中实现函数中断。

例如,如果我有一个按钮并且想要在线程上执行某些操作(运行无限循环),单击此按钮时,我可以这样说:

in thread...
forever
{
  if(button_is_pressed_flag)
  {
    do something...
  }
}

有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

无限循环应该是一个事件循环,然后它可以自动处理跨线程插槽调用,而不必担心细节。

运行代码的习语"不断地"在事件循环中是零持续时间计时器。

我们假设您从代码如下开始:

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();
  ...
}

doWork()要求不要花太长时间 - 也不要太短。如果在现代硬件上花费大约5毫秒,那么它只是在通用应用程序的开销和延迟之间进行正确的权衡。如果在工作线程中需要较低的延迟响应,那么doWork()必须做的工作量减少。 doWork()花费不到1毫秒的时间可能没什么意义。

只要doWork()没有任何事情可做,例如如果它完成了它应该执行的计算,它应该停止使它保持活动的计时器。

您应该将其转换为如下所示:

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();
}

QThread::run中运行的事件循环将通过计时器事件处理程序不断调用doWork。但是,只要需要对生成在该线程中的对象进行跨线程槽调用,事件循环就会将表示槽调用的内部QMetaCallEvent传递给QObject::event,然后执行该调用。

因此,当您在onButtonClick中设置断点时,调用堆栈附近会有QObject::event

答案 1 :(得分:1)

你可以启动一个线程,然后立即等待std :: condition_variable,然后当单击该按钮时(在主线程上调用该事件),通知条件变量并且线程将唤醒。

然而,这有点奇怪。你想做什么?单击按钮时调用异步任务?在这种情况下,也许最好只使用std :: packaged_task或std :: async从按钮单击事件启动一个。