【问题标题】:How to avoid Qt app.exec() blocking main thread如何避免 Qt app.exec() 阻塞主线程
【发布时间】:2014-04-12 22:07:45
【问题描述】:

我是 Qt 新手,但需要解决一个难题。

我创建了一个非常简单的 GUI,我需要将它添加到现有的 C++ 应用程序中。问题是,我只编写了一个模块,它插入到一个更大的架构中,这限制了我对主线程的访问。

我的代码必须位于以下四个函数中: 一个 Init() 函数,它在主线程中运行。 以及在工作线程中运行的 WorkerStart()、WorkerStep() 和 WorkerStop() 函数。

我在 Init() 函数中编写了 QApplication 和 GUI 对象。但当然,在该函数末尾调用 app.exec() 会阻塞整个其余代码。不可行。

我读到的所有内容都说 Qt gui 对象只能在主线程中运行。

所以我的问题是,如何在 init() 函数(主线程)中设置我的 gui,并从那时起仅使用工作线程运行它?

我发现了这个:QApplication In Non-Main Thread

这些解决方案给了我一些不同的行为。方向正确,但不稳定或功能不全。但是我不明白如果qt gui只能在主线程中运行,而这些解决方案将它们放在其他线程中,为什么这些都是解决方案。这样就发送了关于什么可以在其他线程中运行和不可以在其他线程中运行的混合消息,这变得非常混乱。

似乎将 gui 添加到现有 C++ 程序而不将其锁定在 exec() func 中应该是一种相当普遍的情况,所以我觉得我错过了一些明显的东西。有人可以帮我解决这个问题吗?

提前非常感谢。 菲尔

【问题讨论】:

  • QObject::moveToThread,但你肯定是在和这个流对抗。
  • 嗨,马特。感谢您的评论。你能为我详细说明一下吗?涉及许多对象(QApp、QDialog 等)和一些线程。那你说什么应该移到哪里?将所有内容移至我的工作线程?还是从 main 启动一个新的 QThread?还是将 QApp 留在 main 中,然后将其他内容移至新线程?如果这真的不适合 Qt,任何人都可以推荐一个没有此限制的不同 gui 工具包吗?

标签: c++ multithreading qt


【解决方案1】:

大多数时候,“主线程”==“GUI 线程”,所以人们可以互换使用这些术语——甚至官方文档也是如此。我同意这很令人困惑,因为它们不必相同。^ 实际规则是这样的:

只能从实例化QApplication/QGuiApplication的线程访问GUI类

使用像您这样的插件,您需要执行以下操作:

  1. 创建一个新的std::thread(不是QThread
  2. 在该线程中运行init 函数。让它实例化你的QApplication/QGuiApplication 并启动事件循环
  3. 确保您的所有 GUI 对象都只能从该线程访问。

瞧,您现在有一个不是您的主线程的 GUI 线程。


^注意:在 Mac OS X 上是另一回事。由于 Cocoa 框架的限制,主线程必须是 GUI 线程。我上面概述的步骤适用于 Windows/Linux,但不适用于 Mac。对于 Mac,您需要将代码注入主线程——请参阅下面的 Kuba Ober 的 cmets。

【讨论】:

  • 谢谢。这对于理解 main!=gui 线程问题非常有帮助。所以我可以创建一个专用的独立 Qt 线程。不幸的是,我的体系结构中还有其他插件也使用 Qt(特别是 OpenCV qt),并且在调用这两个插件时似乎存在战斗/阻塞。所以我还没有离开树林。我将尝试运行一个 qt 事件循环,它会间歇性地阻止自己的线程。希望这将允许其他 qt 插件玩得很好(时间切片两者)。所以,谢谢你让我越过了第一个障碍。干杯。
  • 在 OS X 上,将代码注入主线程相当简单:dispatch_sync(dispatch_get_main_queue(), ^{ /* do stuff */ }); 您也可以在该线程中运行 Qt 的事件循环,因为它运行本机事件循环。
  • @JKSH 它不会阻塞调用者,但会“阻塞”GUI线程。但由于它会在 GUI 线程上旋转本机事件循环,因此这可能无关紧要。
  • @JeffTrull Qt 实际上并不知道哪个线程是“真正的”主线程; Qt 只记住第一个使用 QObject 的线程。因此,如果您确保 QApplication 是程序中第一个实例化的 Qt 类,那么无论您在哪个线程中,都不会看到该警告。(换句话说:在创建 QApplication 之前不要使用任何 Qt 类. 甚至不要调用任何 Qt 静态方法!)
  • 我会补充一点,对于将来遇到这个问题的任何人,如果你使用 PyQt,仅仅执行“导入”的过程似乎就会触发这个问题。解决方法:甚至在您的 GUI 线程中进行导入 - 避免构建 QObjects 是不够的。
【解决方案2】:

一个很好的解决方案在:git@github.com:midjji/convenient_multithreaded_qt_gui.git

那就是例如

run_in_gui_thread(new RunEventImpl([](){
        QMainWindow* window=new QMainWindow();
        window->show();
    }));

可随时从任何线程调用,同时在后台为您进行设置。

注意,这还负责创建 QApplication 并在线程中执行它。但如果您已经在某个地方这样做过,也可以使用。

【讨论】:

    猜你喜欢
    • 2022-01-24
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-06
    • 2015-05-25
    • 2012-06-06
    相关资源
    最近更新 更多