簡體   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