简体   繁体   English

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

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

In order to learn about threading in Qt and C++ I am creating a small example program.为了了解 Qt 和 C++ 中的线程,我正在创建一个小示例程序。 It has a Gui with a button and a text field:它有一个带有按钮和文本字段的 Gui:

在此处输入图像描述

When the user presses the Calculate button it calculates pi using a sum formula.当用户按下计算按钮时,它会使用求和公式计算 pi。 In order for the Gui to be responsive during this lengthy operation the calculation will be performed in a separate thread.为了让 Gui 在这个冗长的操作期间做出响应,计算将在一个单独的线程中执行。

First I created a subclass of QThread that does the calculation in its run() method and emits the signal void resultReady(double value);首先,我创建了QThread的一个子类,它在其run()方法中进行计算并发出信号void resultReady(double value); when it is finished.当它完成时。 This signal I connected to the slot void setResult(double value);这个信号我连接到插槽void setResult(double value); in my Dialog Gui class. This approach works fine.在我的Dialog Gui class 中。这种方法工作正常。

Now I want to do the same thing using std::thread.现在我想使用 std::thread 做同样的事情。 How do I do this?我该怎么做呢? I am having problems communicating the result back to the Gui.我在将结果传回 Gui 时遇到问题。 I tried this:我试过这个:

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;
};

A StdThreadStrategy object is constructed in the constructor of Dialog :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);
}

I was hoping using QTimer::singleShot in the doTheWork() method would allow me to post to the event queue of the Gui from another thread.我希望在doTheWork()方法中使用QTimer::singleShot singleShot 可以让我从另一个线程发布到 Gui 的事件队列。 Unfortunately I get the error message: QObject::startTimer: Timers can only be used with threads started with QThread .不幸的是,我收到错误消息: QObject::startTimer: Timers can only be used with threads started with QThread

How can I communicate back the result from a std::thread to the Gui main thread in Qt?如何将 std::thread 的结果传回 Qt 中的 Gui 主线程?

There is no general answer to this kind of design problems.这类设计问题没有通用的答案。 I give you just some tips here to show that c++11 provides a higher level of abstraction that can ease the manipulation of thread lifetime.我在这里只给你一些提示,以表明 c++11 提供了更高级别的抽象,可以简化线程生命周期的操作。

In your main GUI thread, run the async task (here I use std::async what gives you a std::future for further manipulation)在您的主 GUI 线程中,运行异步任务(这里我使用std::async为您提供 std::future 以进行进一步操作)

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

Now, your UI is alive, run a Qtimer in the main thread with a callback that will check for the async procedure.现在,您的 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.

Regards.问候。

You could send a custom QEvent that carries the result from the worker thread to the thread your QObject lives in. See QEvent and QCoreApplication::postEvent()您可以发送一个自定义 QEvent,该 QEvent 将工作线程的结果发送到您的 QObject 所在的线程。请参阅QEventQCoreApplication::postEvent()

Create your event class:创建您的活动 class:

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

    double result() {
        return mResult;
    }

private:
    double mResult;
};

Post the event to the event loop and override the event() function to catch it:将事件发布到事件循环并覆盖 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);
    }
...
}

Add a signal to your instance of StdThreadStrategy , and connect that via an explicitly deferred connection to the handler living in the UI thread.向您的StdThreadStrategy实例添加一个signal ,并通过显式延迟连接将其连接到 UI 线程中的处理程序。 That way, you can safely call the signal from any thread, Qt takes care of sending it where it should go.这样,您就可以安全地从任何线程调用信号,Qt 负责将其发送到应该发送的位置 go。

Also don't forget to join() your thread in the destructor of StdThreadStrategy .也不要忘记在StdThreadStrategy的析构函数中join()你的线程。 You also need to be aware that recycling a single instance like that is going to end up in race conditions.您还需要注意,回收这样的单个实例将以竞争条件告终。 Just go and try what happens when you click the button again before pi had been fully computed.只是 go 并尝试在完全计算 pi 之前再次单击该按钮时会发生什么。

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

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