自定义函数中断

Posted

技术标签:

【中文标题】自定义函数中断【英文标题】:Custom function interrupt 【发布时间】:2016-12-04 15:13:06 【问题描述】:

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

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

in thread...
forever

  if(button_is_pressed_flag)
  
    do something...
  

有没有更好的方法?

【问题讨论】:

阅读 Qt 文档中的线程。您的问题并没有给人留下您投入大量精力自己解决问题的印象。 @Silicomancer 你为什么这么认为?这不是一个有效的解决方案吗?我想你不明白我在这里想要达到什么目的...... 【参考方案1】:

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

在事件循环上“连续”运行代码的习惯用法是零持续时间计时器。

假设您从如下所示的代码开始:

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,然后由QObject::event 执行调用。

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

【讨论】:

一个问题:你能解释一下你为什么使用QBasicTimer m_timer;。我只是不明白为什么会有0超时。 A QBasicTimer 只是一个定时器 id 的 RAII 包装器,否则您可以从 QObject::startTimer 获得。 startTimer 是类 C API。由于我们使用 C++ 是有原因的(不是自己编写愚蠢的代码),因此应该使用 QBasicTimer 代替。零超时是一个特殊值。和时间完全没有关系。这意味着:“每次事件队列已被耗尽时发出超时信号”。它导致事件循环永远不会阻塞以等待新事件:它不断发出超时信号。这就像运行您自己的 forever 循环,也会耗尽事件队列。 谢谢!如果可以的话,我也会接受你的评论:)【参考方案2】:

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

但是,这有点奇怪。你想做什么?单击按钮时调用异步任务?在这种情况下,也许最好从带有 std::packaged_task 或 std::async 的按钮单击事件开始。

【讨论】:

我在这里想要实现的是:这个线程正在读取大文本文件(日志),解析它并根据它解析的内容发出信号。这里的问题是我必须能够暂停该线程,停止它,让它解析“更快”(在步骤之间睡得更少)等等......所以条件变量在这里不是那么方便。 你可以通过标志在线程之间进行通信,你只需要在锁定的互斥体中读/写它们 我知道,但如果我有,比如说 100 个标志。它不会再那么好用了,因为中间总会有一点延迟。

以上是关于自定义函数中断的主要内容,如果未能解决你的问题,请参考以下文章

C语言中,用于设置中断、中断信号的函数都有哪些?怎么设置一个发送中断信号(自己定义的)的函数?

STM8S系列基于STVD开发,自定义printf函数+TIM5精确延时函数模块化工程示例

oracle 自定义函数返回一个自定义整数列,如何在下一个自定义函数中,调用上一个函数

Mysql 创建自定义函数

thinkphp 自定义函数放哪里

SQLSERVER里如何自定义函数?