![](/img/trans.png)
[英]In a Qt app, where my worker thread is the “main” thread, how can I communicate with the GUI thread
[英]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 所在的線程。請參閱QEvent和QCoreApplication::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.