微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Qt:在非 GUI 线程中使用 QWidgets

如何解决Qt:在非 GUI 线程中使用 QWidgets

我试图了解在 QWidget 和 Qt 并发方面允许和不允许的内容。我创建了一个 Widget,它有一个 slow_function,我正在考虑三种情况:

  1. 在 GUI 线程上运行 slow_function。这会导致预期的行为;在等待函数返回时,GUI 变得无响应。
  2. 使用QtConcurrent::run(this,&Widget::slow_function)。我很惊讶地看到这并没有阻止 GUI。我已经确认我的实例的线程关联仍然是 GUI 线程,但是,该函数似乎在单独的线程上执行。是否允许这种方法,这是预期的行为(文档链接真的很有帮助)?如果我可以保证 slow_function 是线程安全的,那么这种方法是否安全?
  3. 创建 QThread 的子类,其中包含指向我的小部件的指针。覆盖 run 方法调用 slow_function。行为与案例 2 相同。这也令人惊讶,因为线程关联仍然是 GUI 线程(此外,我们甚至不允许在 movetoThread 上使用 QWidget)。为什么这是在单独的线程上运行? movetoThread 是否意味着只有当我们有兴趣通过从另一个线程发送的信号来调用插槽时才有用?

感谢您的阅读。这是以我的头文件开头的相关代码

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDebug>
#include <QPushButton>
#include <QLayout>
#include <windows.h>
#include <QtConcurrent/QtConcurrent>
#include <QThread>
#include <QApplication>

class Widget;

class Thread: public QThread
{
public:
    Thread(Widget* widget)
        : m_widget(widget){}

protected:
    void run() override;

private:
    Widget* m_widget;
};

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget* parent = nullptr)
        : QWidget(parent),m_thread(this){

        auto layout = new QVBoxLayout(this);

        auto button = new QPushButton("Case 1: Run on gui thread");
        auto button2 = new QPushButton("Case 2: Run with qtconcurrent");
        auto button3 = new QPushButton("Case 3: Run with qthread");

        connect(button,&QPushButton::clicked,this,&Widget::slow_function);
        connect(button2,&Widget::use_concurrent);
        connect(button3,&Widget::use_qthread);

        layout->addWidget(button);
        layout->addWidget(button2);
        layout->addWidget(button3);
    }

    ~Widget()
    {
        m_thread.quit();
        m_thread.wait();
    }

public slots:

    void slow_function()
    {
        qDebug() << "Starting";
        auto gui_thread = QApplication::instance()->thread();
        auto this_thread = thread();
        qDebug() << "Thread affinity is" << (gui_thread == this_thread ? "gui_thread" : "non_gui_thread");
        Sleep(5000);
        qDebug() << "Finished";
    }

    void use_concurrent()
    {
        QtConcurrent::run(this,&Widget::slow_function);
    }

    void use_qthread()
    {
        m_thread.start();
    }

private:
    Thread m_thread;
};



#endif // WIDGET_H

和 main.cpp 文件

#include "widget.h"

#include <QApplication>

void Thread::run()
{
    m_widget->slow_function();
}

int main(int argc,char *argv[])
{
    QApplication a(argc,argv);
    Widget w;
    w.show();
    return a.exec();
}

my_gui

解决方法

你不应该在非主线程中做任何 UI 的事情。用户界面意味着

  • 小部件交互
  • 模型

在 GUI 线程上运行 slow_function

这是不允许的,任何东西都不应该阻塞 GUI 线程。

如果我能保证slow_function是线程安全的,那么这种方法安全吗?

在不同的线程中运行慢函数是可以的。但是...

  • 当用户关闭应用程序但函数仍然执行时会发生什么?

我已经这样做了,但我更喜欢将它封装在一个单独的类中。

创建一个 QThread 的子类,它包含一个指向我的小部件的指针。

如果子类是线程,您应该只继承 QThread。比如,将一个动物类子类化会给你一个动物,而不是四条腿的椅子。

moveToThread 是否意味着只有当我们有兴趣通过从另一个线程发送的信号调用插槽时才有用?

moveToThread 改变对象的亲和性。因此,槽总是在正确的线程中执行,当您调用 invokeMethod 时,该方法将在正确的线程中执行。当一个事件被传递时,事件处理程序总是在适当的线程中被调用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。