【问题标题】:The canonical way to use a QTimer in a separate thread在单独的线程中使用 QTimer 的规范方法
【发布时间】:2019-03-12 11:27:28
【问题描述】:

我打算创建一个工作线程,它将以 1 秒的间隔更新 QTWidget(将其视为数字时钟或进度条更新器可能是一个更好的类比给应用程序)。

我的应用程序是特定于航空电子设备的,它计算飞行路径上的飞行跟踪信息,并在飞行过程中以 1 秒的间隔定期更新地图或其他一些 QT 小部件(我想您可以将其视为一种进度bar - 在我的情况下,进度条可以被地图或表格小部件替换)。该线程可能需要大约 10-20 秒来计算整个飞行数据 - (但是它几乎可以立即获得第一个结果) - 这些结果需要以 1 秒的间隔发送到 GUI 以更新飞机的位置在地图上。

对于合并更新工作线程的计时器的正确和错误方法存在很多混淆。我发现的最好的知识来源是一个 QtCon talk entitied Multithreading with Qt - Giuseppe D’Angelo - 似乎首选的方法是让一个工人 QObject 不继承 QThread 并且让计时器在它自己的事件处理程序中运行 -但是关于将工作线程移动到另一个我不太了解的线程有很多困惑,我正在寻找一些关于如何正确执行此操作的规范建议。在 Lambda 中这样做是理想的,因为我可以保持逻辑和线程相对隔离,这将是理想的。

我正在寻找是否有人可以为我指出类似示例的方向(尤其是如果它使用现代 Lambdas)。我目前使用的是 QT 5.11.2。

我的worker对象(其中包含QTimer如下):

class SimulatedFlightWorker : public QObject
{
    Q_OBJECT
public:
    explicit  SimulatedFlightWorker()
        : mpMMRFlightData{}
    {}
public slots:
    // worker thread slot that computes the mpMMRFlightData
    void importSimulatedFlight(
        const QString& fileName,
        const int aPlaybackSpeed);
    void updateGUI(int secondsElapsed);
signals:
    // every second during the flight we need to send a new MMR
    // record to the GUI - to display on a spreadsheet or google map
    void updateFlightData(const MMRTimedRecord& rMMRRecord);

    // this is effectively when the worker thread is done
    void flightCompleted();
private:
    QTimer mTimer;
    // the 
    std::unique_ptr<MMRFlightData> mpMMRFlightData;
};

这是我设置信号和插槽的代码(还没有工作)

// Experimental worker model adapted from YouTube tutorial
// by Giuseppe di Angelo https://www.youtube.com/watch?v=BgqT6SIeRn4
auto thread = new QThread;
auto worker = new SimulatedFlightWorker;


connect(thread, &QThread::started, worker, &SimulatedFlightWorker::importSimulatedFlight);
connect(worker, &SimulatedFlightWorker::flightCompleted, thread, &QThread::quit);
connect(thread, &QThread::finished, worker, &SimulatedFlightWorker::deleteLater);

worker->moveToThread(thread);
thread->start();

【问题讨论】:

  • 为每个计时器滴答声创建一个新线程在性能方面可能是个坏主意,这意味着很多上下文切换,因此,您可以尝试以下方法:1. QThreadPoolQRunnable , 2. 或QtConcurrent::run()doc.qt.io/qt-5/qtconcurrentrun.html,使用QFutureWatcher 使future 真正异步并使用信号和槽doc.qt.io/qt-5/qfuturewatcher.html 分派结果。 future 方式是现代方式,因为它有点像 JavaScript 的 promises 和 Dart 的 futures,还要注意:任务将NOT被执行直到线程可用。跨度>
  • @user9335240 我只想在 SimulatedFlightWorker 的后台运行 1 个 QTimer,它会发出超时信号,如下所示 connect(&mTimer, &QTimer::timeout, &this, &SimulatedFlightWorker ::更新飞行数据)。不太确定这种语法,但我猜我必须在我的工人的构造函数中的某个地方这样做
  • 尝试使用QtConcurrent::run([=](){ /* Execute your lambda here */ });这将返回一个QFuture,QFutureWatcher&lt;int&gt; watcher; connect(&amp;watcher, SIGNAL(finished()), &amp;myObject, SLOT(handleFinished())); // Start the computation. QFuture&lt;int&gt; future = QtConcurrent::run([=](){ /* Execute your lambda here */ }); watcher.setFuture(future);

标签: c++ multithreading qt qthread qtimer


【解决方案1】:

其实很简单:

  1. 您的计算是在 QObject 派生类中完成的

  2. 通过该对象中的信号发出更新。

  3. 计算以小块完成,耗时 10-20 毫秒。

  4. 计算是从零持续时间的计时器触发的——它不是计时器,而是一种检测线程中事件循环空闲状态的方法。

  5. 更新由 1 秒计时器触发。

这个类可以在任何线程上运行——它甚至可以在 GUI 线程上运行,但会使其响应性稍差(但不会完全不响应)。

您通常会实例化该类,开始计算,然后将其移至专用线程。

【讨论】:

  • @KuberOber 感谢您的规则,我遇到一点麻烦的地方是如何正确初始化 QObject 派生类中的计时器,我是否将其作为类的成员嵌入以及如何我正确初始化它。如果你有一个小例子来展示如何将它添加到一个简单的 GUI 中,那就太好了。
  • 添加QTimer 类型的成员。它必须归类所有。无需动态分配。例如,在类的主体中:QTimer m_updateTimer{this};
  • 任何机会你都可以把小例子放在一起来展示我如何使用它 - 我是一个相对的 QT 新手,但在设置正确的信号/插槽语法以使其工作时仍然遇到问题正确。在我的主窗口类中,我是否需要一个专用的 Qthread 成员,我应该将线程移动到(我不想干扰 GUI 线程)。我想在 on_button_xxx 插槽中启动 worker。
  • 你也是 C++ 新手吗?这有很大的不同。在所有这些中几乎没有特定于 Qt 的内容。 QThread 是一个可为空的线程句柄(线程停止时为空),您可以像使用任何其他资源句柄一样使用它。
  • 新手评论对回答这个问题没有帮助。话虽如此,我在一个独立的例子上取得了一些进展。我有 1 个问题,我的常规 1 秒发射更新没有被转发到 GUI 线程。 bool connected = connect(&mWorkerThread, &QThread::started, worker, &Worker::doWork); //connected = connect(worker, &Worker::progressUpdate, [=](const MMRTimedRecord&) {}); // 这工作(换行) connected = connect(worker, &Worker::progressUpdate, this, &mainwindow::updateSimulatedFlight, Qt::QueuedConnection);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 2015-01-21
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
相关资源
最近更新 更多