【问题标题】:Should I call processEvents() on a thread?我应该在线程上调用 processEvents() 吗?
【发布时间】:2020-04-25 03:59:50
【问题描述】:

QThread 文档提出了两种让代码在单独线程中运行的方法。如果我使用moveToThread 方法,我必须调用processEvents() 来发出超时,以执行lambda。这似乎花费了大量的CPU。为什么会这样?

class Worker : public QObject
{
    Q_OBJECT
    QTimer* timer;
    bool m_abort = false;
public:
    Worker() {}
    void abort() {m_abort = true;}

public slots:
    void run() {
        timer = new QTimer;
        connect(timer, &QTimer::timeout, []{qDebug() << "computed";});
        timer->start(1000);
        forever {
            if (m_abort) break;
            QCoreApplication::processEvents();
        }
    }
};

class MainWidget : public QWidget
{
    Q_OBJECT
    QThread thread;
    Worker* worker;
public:
    MainWidget()
    {
        worker = new Worker;
        worker->moveToThread(&thread);
        connect(this, &MainWidget::start, worker, &Worker::run);
        thread.start();
        emit start();
    }
    ~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
    void start();
};

但是,如果我将 QThread 子类化并重新实现 run(),则无需调用 processEvents。 CPU成本似乎更低。为什么?

class Worker : public QThread
{
public:
    Worker() {}

protected:
    void run() override {
        QTimer timer;
        connect(&timer, &QTimer::timeout, []{qDebug() << "computed";});
        timer.start(1000);
        exec();
    }
};

class MainWidget : public QWidget
{
    Q_OBJECT
    Worker* worker;
public:
    MainWidget()
    {
        worker = new Worker;
        worker->start();
    }
};

【问题讨论】:

  • 恕我直言,调用processEvents() 几乎总是一个错误。通常总会有更好的方法。与嵌套事件循环相同(有时通过使用带有.exec() 的对话框间接)。这些都是最终导致 Qt 项目痛苦的事情,应该避免。
  • 为什么你需要使用这个timer 以及为什么你需要forever 循环用于worker?你搞砸了一些东西,我不知道如何解决它,因为我看不出你到底想要达到什么目的。很可能您根本不需要线程。我怀疑XY problem 所以请提供更多背景信息。请解释为什么你认为你需要一个多线程?
  • 在我看来,您的第一个解决方案是“忙于等待”。 en.wikipedia.org/wiki/Busy_waiting ... 不是您想做什么!
  • 我建议检查 Bo Thorsen 的 QtWS19 threading talk!也许您会找到更好的解决方案。
  • 仅供参考,您应该至少使用 std::atomic_bool 而不是 bool 来管理您的循环生命周期。

标签: c++ multithreading qt qthread qtimer


【解决方案1】:

您的 run() 函数“阻塞”了线程。它在线程上下文中被调用,但永远不会返回。这意味着,一旦调用了 run() 函数,线程中的事件循环就不再执行。

要让计时器事件调用您的 lambda,必须处理事件循环。 如果你想像这样修改你的运行函数:

void run() {
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, []{qDebug() << "computed";});
    timer->start(1000);

    // don't loop here, instead exit the function 
    // and let the thread return back to the event loop
}

那么你的 lambdas 应该被调用。该线程也将继续运行,直到您调用 thread.quit()

注意:也可以直接连接线程的'''started'''信号:

connect(&thread, &QThread::started, worker, &Worker::run);
thread.start();

【讨论】:

    【解决方案2】:

    moveToThread 方法可以通过在线程发射开始后调用 run() 函数来改进。

    但我仍然不知道为什么我最初所说的方式不起作用。

    【讨论】:

      猜你喜欢
      • 2011-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-02
      • 1970-01-01
      • 1970-01-01
      • 2012-02-21
      • 2011-10-11
      相关资源
      最近更新 更多