【问题标题】:QT Threading issues... something is stalling GUI responseQT 线程问题...某些东西阻碍了 GUI 响应
【发布时间】:2014-07-21 12:00:53
【问题描述】:

我遇到问题,QT 线程以某种方式停止了主 GUI。

回答第一个问题...是的,QThread 不是子类,而是以“正确的方式”完成的。

线程长期运行(不是“do x then exit”),它的主循环确实有延迟/休眠。

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

我认为有些编码领域可能会造成问题。

我正在使用我自己的“睡眠”功能,因为 msleep 不可用(仅 QThread 专用)。 有人能看出下面的代码有什么问题吗?

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

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

我会检查 Qt 信号队列长度,但我不知道如何做到这一点?

提前致谢!

【问题讨论】:

  • 像这样运行自己的伪事件循环并不适合 Qt 自己的事件循环;如果可以,更好的方法是将代码组织成一系列槽方法调用,而不是调用 tSleep(),而是执行 QTimer::singleShot(ms, this, SLOT(MyNextFunction())) 和返回,以便 MyNextFunction() 将在适当的时间被调用(MyNextFunction() 然后将执行过去的 tSleep() 调用之后的代码)。

标签: c++ multithreading qt signals-slots qthread


【解决方案1】:

您的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 的使用情况。

以 16fps 的速度推送 16kbytes 不算什么。

【讨论】:

    【解决方案2】:

    改变您的应用程序的观点也会有所帮助。您可以创建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();
    

    希望这可能会有所帮助。

    【讨论】:

    • 只是说明显而易见的事情:永远不要断言()连接(),否则您的代码将在发布模式下静默失败...所以请注意@xtofl 的话(简短且仅显示设计提示),不要简单地复制此代码。
    猜你喜欢
    • 2021-01-02
    • 2014-05-31
    • 2013-02-17
    • 1970-01-01
    • 2023-02-11
    • 1970-01-01
    • 1970-01-01
    • 2016-12-20
    • 1970-01-01
    相关资源
    最近更新 更多