从不同的 std::thread 更新 Qt GUI

Posted

技术标签:

【中文标题】从不同的 std::thread 更新 Qt GUI【英文标题】:Updating Qt GUI from a different std::thread 【发布时间】:2018-03-27 10:06:27 【问题描述】:

在我希望摆脱 Qt 依赖的应用程序的一个单独组件中,我使用 std::thread 进行某些操作。我想在处理过程中对我的主应用程序进行更改。为此,我尝试将一个函数(即:function<void(UpdateNode*)> nodeUpdatedCallback)从我的应用程序传递给我的组件。

此函数更新 UI,但由于我从另一个线程调用该函数,Qt 说我无法从非主线程访问 UI。

我看过很多文章通过使用QThread 和信号来解决这个问题,创建一个工作线程并将其移动到该线程。

既然我想使用std::thread,是否可以使用std::thread更新基于Qt的UI?

【问题讨论】:

你不能把函数传递给GUI线程并在那里执行吗? 问题是我想从后台线程调用函数(nodeUpdatedCallback),而它正在工作 这正是它的意思:你将一个函数传递给 GUI 线程并在那里执行。从哪个线程传递它并不重要 - 重要的是它传递到哪个线程 【参考方案1】:

当我在多线程环境中使用 Qt 并且不想/不能使用惯用的信号/槽时,我将以下方法添加到我的对象中:

    typedef std::function<void()> function_t;
    void executeInObjectsThread(function_t const&);

private slots:
    void executeInObjectsThreadSlot(function_t);

以及定义:

void MyObj::executeInObjectsThread(function_t const& f)

    QMetaObject::invokeMethod(
        this,
        "executeInObjectsThreadSlot",
        Qt::QueuedConnection,
        Q_ARG(function_t,f)
    );

然后在您的其他线程中调用

foo->executeInObjectsThread([=]
    foo->addWidget(new QWidget(foo));
    // ...
    foo->editBox->setText(QString::number(currentResult));
    foo->progressBar->setValue(n);
);

【讨论】:

请参阅this question了解此问题的详细处理方式。【参考方案2】:

基本上,您永远不应该从另一个线程修改任何与 GUI 相关的东西。这有一个很好的理由:GUI 依赖于非线程安全的事件循环。您开始使用它,并且由于竞争条件,您将获得未定义的行为。感谢 Qt 保护您免于搞砸!

但是,您的问题的解决方案非常简单。在您的情况下,我所做的是定义线程修改的公共变量。所以我使用QTimer 来检查线程是否完成了它的工作。要知道线程是否已完成,您可以使用std::atomic&lt;bool&gt; 标志,该标志将在计算结束时设置(或使用std::promise/future,我更喜欢)。然后,GUI 将只从主线程读取(也是线程安全的)结果并显示它们。这是 100% 安全的。

最近我用 Neblio 做了这个。 Check out my code there 用于在另一个线程中运行并在 GUI 上显示结果的自动更新程序。

【讨论】:

【参考方案3】:

Qt 确实使用了信号/槽机制。您只提到了信号部分,但这里插槽也很重要。插槽存在于对象中,如果该对象是具有线程亲和性的 Qt 对象,则插槽将在该线程中执行。即使信号来自另一个线程也是如此。

当插槽所有者是QWindow 或与 UI 线程关联类似的东西时,这特别有用。它确保 UI 代码在正确的线程中运行。几乎可以肯定,UI 线程是您应用程序的 main() 线程。

所以您仍然可以从您的std::thread 收到信号。它不会导致插槽在相同的std::thread 中运行。

【讨论】:

以上是关于从不同的 std::thread 更新 Qt GUI的主要内容,如果未能解决你的问题,请参考以下文章

std::thread 消耗 Qt 中的所有 CPU

从另一个 std::thread 调用 Qt 对象方法

正如我所料,Qt GUI 不适用于 std::thread

如何在 Qt 的主事件循环中使用 std::thread?

具有与 std::thread 不同的线程库的 C++ thread_local

围绕函数调用 std::thread() 的工作方式不同