繁体   English   中英

如何将 std::thread 的结果传回 Qt 中的 Gui 主线程?

[英]How can I communicate back the result from a std::thread to the Gui main thread in Qt?

为了了解 Qt 和 C++ 中的线程,我正在创建一个小示例程序。 它有一个带有按钮和文本字段的 Gui:

在此处输入图像描述

当用户按下计算按钮时,它会使用求和公式计算 pi。 为了让 Gui 在这个冗长的操作期间做出响应,计算将在一个单独的线程中执行。

首先,我创建了QThread的一个子类,它在其run()方法中进行计算并发出信号void resultReady(double value); 当它完成时。 这个信号我连接到插槽void setResult(double value); 在我的Dialog Gui class 中。这种方法工作正常。

现在我想使用 std::thread 做同样的事情。 我该怎么做呢? 我在将结果传回 Gui 时遇到问题。 我试过这个:

class StdThreadStrategy : public QObject {
public:
    void doTheWork() {
        double pi = pi_sum();
        QTimer::singleShot(0, this, [=] { dialog->setResult(pi); });
    }
    // This is called by Dialog when the user presses the Calculate button:
    void calculatePi(Dialog* dialog) {
        this->dialog = dialog;
        std::thread t(&StdThreadStrategy::doTheWork, this);
        thread = std::move(t);
    }
private:
    Dialog* dialog;
    std::thread thread;
};

Dialog的构造函数中构造了一个StdThreadStrategy object :

Dialog::Dialog() : QDialog() {
  // .. create gui code
  calculatePiStrategy = new StdThreadStrategy();
}

// this is the slot I want called from the other thread:
void Dialog::setResult(double value) {
  piLineEdit->setText(QString::number(value));
}

// Called by Calculate button:
void Dialog::calculate() {
  calculatePiStrategy->calculatePi(this);
}

我希望在doTheWork()方法中使用QTimer::singleShot singleShot 可以让我从另一个线程发布到 Gui 的事件队列。 不幸的是,我收到错误消息: QObject::startTimer: Timers can only be used with threads started with QThread

如何将 std::thread 的结果传回 Qt 中的 Gui 主线程?

这类设计问题没有通用的答案。 我在这里只给你一些提示,以表明 c++11 提供了更高级别的抽象,可以简化线程生命周期的操作。

在您的主 GUI 线程中,运行异步任务(这里我使用std::async为您提供 std::future 以进行进一步操作)

auto fut = std::async(std::launch::async, [=]() {
            // do some work
        });

现在,您的 UI 已激活,在主线程中运行 Qtimer,并使用回调检查异步过程。

// callback content will be something like this
// checking via the future the state of the async task
if (fut.wait_for(std::chrono::milliseconds(25)) !=
                           std::future_status::ready) {
   // not yet finished
   return;
}

// No do what you want with the result
auto res = fut.get();
// emit a signal to refresh the gui etc.

问候。

您可以发送一个自定义 QEvent,该 QEvent 将工作线程的结果发送到您的 QObject 所在的线程。请参阅QEventQCoreApplication::postEvent()

创建您的活动 class:

class MyEvent : public QEvent
{
public:
    MyEvent(double result) : QEvent(QEvent::User), mResult(result) {
    }

    double result() {
        return mResult;
    }

private:
    double mResult;
};

将事件发布到事件循环并覆盖 event() function 以捕获它:

class StdThreadStrategy : public QObject {
...
    void doTheWork() {
        double pi = pi_sum();
        QCoreApplication::postEvent(this, new MyEvent(pi));
    }
...
    bool event(QEvent *event) override {
        if (event->type() == QEvent::User) {
            const auto result = static_cast<MyEvent*>(event)->result();
            dialog->setResult(result);
        }
        return QObject::event(event);
    }
...
}

向您的StdThreadStrategy实例添加一个signal ,并通过显式延迟连接将其连接到 UI 线程中的处理程序。 这样,您就可以安全地从任何线程调用信号,Qt 负责将其发送到应该发送的位置 go。

也不要忘记在StdThreadStrategy的析构函数中join()你的线程。 您还需要注意,回收这样的单个实例将以竞争条件告终。 只是 go 并尝试在完全计算 pi 之前再次单击该按钮时会发生什么。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM