QT线程问题…某些东西正在拖延GUI响应

QT Threading issues... something is stalling GUI response

本文关键字:响应 GUI 问题 线程 QT      更新时间:2023-10-16

我有QT线程问题,不知何故会使主GUI停滞。

回答第一个问题…是的,QThread不是子类化的,并且是以"正确的方式"完成的。

线程是长期运行的(不是"do x然后退出"),它的主循环确实有延迟/睡眠。

线程的主要目的是从其他地方收集数据,组成图像,并通过COM端口传输该图像。

我认为有一些编码区域可能会产生问题。

我使用我自己的"睡眠"功能,因为msleep不可用(仅对QThread私有)。有人能看到下面的代码有什么问题吗?

void SendImageJob::tSleep(int ms)
{
  QElapsedTimer   timer;
  timer.start();
  while ((timer.elapsed() < ms) && !abort)
  {
      QCoreApplication::processEvents();
  }
}

第二个可能是问题的是通过信号/插槽发送到/从线程发送的数据量。通过信号发送的图像数据大小约为16KB,以15fps的速度发送。对于排队的连接信号来说,这是否太多了?

我会检查Qt信号队列长度,但我不能找出这是如何做到的?

提前感谢!

您的sleep代码没有任何问题,只是您根本不需要使用它。在Qt 5中,线程的sleep方法不再是私有的。在Qt 4中,您可以轻松地绕过它。无论如何,您需要使用这个安全的QThread包装器来获得真正的RAII优点,因此您不妨在同一个类中公开静态睡眠方法:

class Thread : public QThread {
  using QThread::run; // final, no subclassing
public:
  Thread(QObject * parent = 0) : QThread(parent) {}
  ~Thread() { quit(); wait(); }
  using QThread::sleep;
  using QThread::msleep;
  using QThread::usleep;
};

您的部分问题可能是您正在使用阻塞QObject,这是一个稍微落后的设计。除非你一直在使用阻塞api(比如一个坏了的数据库接口库),否则你的对象应该在任何线程上都能令人满意地执行,包括GUI线程。只有将它移动到一个单独的线程中,才能减少GUI线程的延迟。

合理实现它的一种方法是利用异步:

class Worker : public QObject {
  Q_OBJECT
  typedef void (Worker::*State)();
  QBasicTimer m_timer;
  State m_nextState;
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() != m_timer.timerId()) return;
    m_timer.stop();
    (this->*m_nextState)();
  }
  void wait(State state, int ms) {
    m_nestState = state;
    m_timer.start(this, ms);
  } 
  void state1() {
    ...
    wait(&Worker::state2, 100); // wait 100ms and continue in state2     
  }
  void state2() {
    ...
  }
public:
  Worker(QObject * parent = 0) : QObject(parent), m_nextState(&Worker::state1) {
    m_timer.start(this, 0);
  }
};
如果您可以使用Qt 5和c++ 11 - lambdas来拯救,这样的代码将变得更清晰。您还可以研究QStateMachine的使用。

更改应用程序的视点也会有所帮助。而不是"在线程中做一些事情",你可以创建你的DataSource类和移动QThread。这个数据源应该负责创建一个帧(通过一个onCreateFrame插槽),并且可以被一个QTimer每秒触发15次。

让Qt完成等待:)

简短且仅显示设计的暗示:

QApplication app;
auto datasource = new DataSource(&app);
auto comsender = new ComPort("COM1", &app); // or whatever your class
QTimer fpstimer;
fpstimer.setInterval(1000/15);
check_for_success(QObject::connect(
   datasource, DataSource::data, 
   comsender, ComPort::sendData));
check_for_success(QObject::connect(
   &fpstimer, QTimer::timeout, 
   datasource, DataSource::onCreateFrame));
// make sure everything runs on it's own thread
datasource->moveToThread(new QThread(&app));
comsender->moveToThread(new QThread(&app));
app.exec();