我怎样才能等到 Qt 中的 keyevents 中的每个进程都完成?

How can i wait until the every process finished in keyevents in Qt?

我创建了一个按键事件,如果我按下 "A" 键,它将执行函数 A()。A() 的过程将持续 2 秒。如果我像 4 次/2 秒那样快地按下键,我想等到每个过程都完成。我测试发现,如果我按 4 次/2 秒的键,它会在以后的按键事件中执行该过程 firstly.How 我可以等到每个过程在 keyevents 中完成吗?我必须尝试使用​​线程和互斥体。但是出了点问题。第一次用mutex.I不知道怎么解决这个问题

int g = 0;
void MainWindow::keyPressEvent(QKeyEvent *event)
{
 int keyCode = event->key();
 if(keyCode == Qt::Key_A) {
    qDebug() << "da";
    a->start();
 }
}


void MyThread::run()// i try to block the second time process while press the key so quickly
{

  mutex->lock();
  ...//process:last for 2s
  g++;
  mutex->unlock();
}

不清楚需要什么:

  • 如果您想为每个按下的键启动一个线程,请使用 QtConcurrent::run(A); A()
  • 中没有任何互斥锁
  • 如果你想为每个按下的键执行 A() 但不是同时使用 QtConcurrent::run(A); 并在 A() 中使用互斥锁(就像你所做的那样)

如果我没理解错的话,您使用的是要在按下按钮 A 时执行的操作 A()。但是,您在单独的线程中执行操作 A(),因为您不想阻塞 GUI 线程并冻结您的 UI.

如果您不想 "skip" 按钮按下,解决方案很简单:

  1. 创建一些名为 context 的虚拟 QObject
    QObject* context = new QObject;
  2. QThreadthread 起来 运行。
    QThread* thread = new QThread{ this }; thread->start(); ...
  3. 然后将 context 移动到 thread object->moveToThread(thread).
  4. MainWindow class 中创建信号,比如 runA()
  5. 将此信号与 A() 操作连接:
    connect(this, &MainWindow::runA, context, [] () { A(); });
  6. 每次按下按钮 "A" 都会发出这个信号:emit runA();

每次发出信号时,都会将要执行的操作 A() 的事件发布到“线程事件循环”。所有事件将按发布顺序进行处理。


#pragma once

#include <QThread>
#include <QDebug>
#include <QWidget>
#include <QKeyEvent>

class MainWindow : public QWidget
{
    Q_OBJECT
private:
    QThread* thread;
    QObject* context;

public:
    MainWindow()
        : thread{ new QThread },
          context{ new QObject }
    {
        context->moveToThread(thread);
        connect(this, &MainWindow::doAction, context, [this] () {
            Action();
        });
        thread->start();
    }
    ~MainWindow() {
        context->deleteLater();
        thread->deleteLater();
    }

signals:
    void doAction();

public:
    void keyPressEvent(QKeyEvent* event)
    {
        int keyCode = event->key();
        if(keyCode == Qt::Key_A) {
            emit doAction();
        }
    }

    void Action() {
        qDebug() << "Action is being executed.";
        QThread::currentThread()->sleep(2); // imitate some long calculation
    }
};

无论您按下按钮的速度有多快"A"。 "Action is being executed." 将以恰好 2 秒的间隔打印,并且恰好按下按钮的次数。

一个快速的解决方案是在只有一个线程的线程池上使用 QtConcurrent::run。这使您不必管理线程的生命周期,这是一种昂贵的资源 - 例如闲置一段时间后将被丢弃以释放资源。

// https://github.com/KubaO/Whosebugn/tree/master/questions/single-job-lambda-45913311
#include <QtWidgets>
#include <QtConcurrent>

class LogWindow : public QPlainTextEdit {
   Q_OBJECT
   QThreadPool m_pool;
   int g = {}; // can be accessed from the worker thread only
   void keyReleaseEvent(QKeyEvent * event) override {
      if (event->key() == Qt::Key_A)
         QtConcurrent::run(&m_pool, this, &LogWindow::method);
      QPlainTextEdit::keyReleaseEvent(event);
   }
   /// This method must be thread-safe. It is never reentered.
   void method() {
      QThread::sleep(2); // block for two seconds
      g++;
      emit done(g);
   }
   Q_SIGNAL void done(int);
public:
   LogWindow(QWidget * parent = {}) : QPlainTextEdit{parent} {
      appendPlainText("Press and release 'a' a few times.\n");
      m_pool.setMaxThreadCount(1);
      connect(this, &LogWindow::done, this, [this](int val){
         appendPlainText(QString::number(val));
      });
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   LogWindow w;
   w.show();
   return app.exec();
}

#include "main.moc"