当从外部对象调用方法时,qt 的 gui 线程是不是会在后台生成线程?

Posted

技术标签:

【中文标题】当从外部对象调用方法时,qt 的 gui 线程是不是会在后台生成线程?【英文标题】:does qt's gui thread spawn threads under the hood when calling methods from outside objects?当从外部对象调用方法时,qt 的 gui 线程是否会在后台生成线程? 【发布时间】:2018-05-14 11:35:54 【问题描述】:

我最近在用 c++ 开发基于 Qt 的应用程序时遇到了线程/内存问题,我正在寻找正确的解释。我不能真正发布一个功能齐全的示例,因为这需要链接到 Qt 等。但是用几行简短的代码可以很清楚地解释这个问题。

当我单击 gui 上的按钮时,会发生以下情况:

void MainWindow::onClick()

    std::vector<int> vec;
    vec.push_back(0);

    dev.connect(vec);

    // do some more stuff

在这种情况下,devMainWindow 的成员,并且属于Device 类类型,它代表我要连接的硬件(或更准确地说,硬件驱动程序)。 connect 的代码类似于:

void Device::connect(const std::vector<int>& vec)
    // do some stuff with vec that takes a long time

我遇到的问题是设备驱动程序向我抛出异常,因为它们从 vec 中获取了错误的值。事实上,当我闯入connect 时,数据消失了:在那个范围内vec 是空内存。我使用shared_ptrs 解决了这个问题。

我的理论是,当我从 GUI 线程调用 dev.connect(vec) 时,Qt 实际上将该调用放在单独的线程上。然后,该函数需要很长时间,Qt 决定是时候继续并完成onClick(或类似的事情,也许它会立即发生)所以当vecDevice::connect 处理时,它已经超出范围了。这符合 shared_ptr 在这里节省一天的事实。

所以我的问题是,我对此是否正确?有人可以解释Qt隐式线程行为的细节还是指出一些这样的解释?

【问题讨论】:

我相信它确实会产生(Qt 专用)线程。您可以使用调试器(Linux 上pthread_create上的断点)找出答案。 This 是 GTK 的类似问题 Qt 做了一些隐式线程,但在这种情况下它不应该影响您 - MainWindow 的对象实例的 onClick 信号和您的插槽位于同一个对象上,因此发送/接收 qt 信号将在同一个对象上完成线程(除非您专门通过 Qt::Blocking/QueuedConnection)。您的设备类是从 QObject 派生的吗?如果是这样,请检查您没有隐式调用超类的 QObject 的 connect() 函数。您的 vec 何时超出范围?我假设您最后一个 vec.push() 和 dev.connect(vec) 之间的汇编代码足够短,可以提供答案。 绝对不,Qt 永远不会代表您做出这样的决定,文档warns many times 关于在 GUI 线程中做繁重的工作。相反,他们鼓励您(使用 Qt 的开发人员)offload heavy job to worker threads。像这样的事情永远不会隐式地完成,因为它总是会产生非常糟糕的副作用(正如你在问题中提到的那样)...... " 我不能真正发布一个功能齐全的示例,因为这需要链接到 Qt 等。" 1、2、3、...)。您只需将我的答案中的代码复制并粘贴到main.cpp 文件中即可运行它... 【参考方案1】:

您要问的是,QT ui 线程是否有可能在某些任务中花费这么长时间,以至于它被中断、从函数返回,然后尝试从中断的地方恢复。

答案是否定的。如果一个线程被中断,那么它将要么返回到原来的位置,要么即将终止。尝试不这样做可能会被病毒扫描程序标记并引入令人讨厌的讨厌的错误。

如果您尝试在 UI 线程中执行需要很长时间的操作,那么可能发生的情况是您的 UI 变得无响应,并且操作系统会抱怨应用程序变得无响应(因为应用程序不再能够对操作系统发送给它的事件做出反应)。

更有可能的是,当 QT 中发出信号时,无法保证它会立即传递到插槽,因此您最终会遇到您所描述的情况。

shared_ptr '拯救了一天'的事实意味着你没有看到你正在破坏你的堆栈的情况(一件好事,它们很难调试);一个简单的堆栈跟踪应该可以回答您的问题。

【讨论】:

【参考方案2】:

对 UKMonkey 加一,因为答案确实是“不”。感谢您提供详细信息。

进一步调查,我发现实际上还有另一个问题一直是设备驱动程序投诉的核心。我认为这是向量的东西,因为(在这种特殊情况下)空向量可能是异常的根源并且调试器将其显示为空。使用shard_ptrs,我看到了向量中的值,但仍然有异常。然后,我找到了异常的(不相关且真实的)原因并修复了它。回到 non-shared_ptr 版本(但不相关的异常抛出错误被压扁),代码按预期运行。

所以,这只是一条红鲱鱼。但是,我很高兴我知道答案是“不”。在处理诸如此类的直截了当的场景时,Qt 不会在线程方面做任何有趣的事情。我展示的代码确实阻塞了对connect的调用。

【讨论】:

以上是关于当从外部对象调用方法时,qt 的 gui 线程是不是会在后台生成线程?的主要内容,如果未能解决你的问题,请参考以下文章

Qt多线程编程总结(所有GUI对象都是线程不安全的)

qt5 通过moveToThread方式创建的线程,主线程(GUI)是不是会控制工作线程

qt多个线程调用同一个类怎么处理

将 Qt GUI 拆分为多个线程用于 GUI、模拟和 OpenGL 是不是可行?

qt如何让控件在单独线程运行

为啥gui线程在槽中调用QLabel控件的setText方法响应慢?