![](/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.