首页 > 其他分享 >Qt moveToThread使用及注意事项

Qt moveToThread使用及注意事项

时间:2023-07-29 10:44:47浏览次数:36  
标签:moveToThread son Qt 对象 Father QObject 线程 注意事项 Son

在Qt中,每个QObject对象都有一个线程关联,这个线程被称为对象的“线程上下文”。默认情况下,一个QObject对象的线程上下文与创建它的线程相同。也就是说,如果我们在主线程中创建了一个QObject对象,那么这个对象的线程上下文就是主线程。

在某些情况下,我们可能需要将一个QObject对象(或继承QObject的对象)移动到另外一个线程中执行,这时就可以使用QObject的moveToThread函数。调用该函数后,会切换这个QObject对象及其子对象的线程上下文切换到新的线程中,这意味着这个对象的所有信号和槽函数都会在新的线程中执行。这样耗时的工作就会在另一个线程中执行,不会卡主界面。

注意:子对象的含义是指QObject对象内创建的QObject对象,这个内部创建的对象的父对象是QObject对象,即内部对象初始化时把this指针传入作为父对象指针。

 

基本使用

查看代码
QThread* thread = new QThread(); // 创建一个新线程
Worker *my_worker= new Worker; // 创建一个工作耗时的对象,继承QObject类
my_worker->moveToThread(thread); // 将my_worker对象移动到新线程中
//线程析构时发出finished信号,此时连接的槽函数可以用来释放线程中的工作类
connect(&thread, &QThread::finished, my_worker, &QObject::deleteLater);
thread.start(); //启动线程,并进入事件循环

 在这个例子中,由于my_worker对象没有实现任何槽函数,所以在新线程中并没有什么可执行的代码。但是,如果这个对象有槽函数,那么这些槽函数就会在这个新线程中执行。

 

在使用moveToThread函数时,需要注意以下几点:

  • 只有QObject对象可以使用moveToThread函数,其他对象不能使用。
  • 一旦调用了moveToThread函数,这个对象的线程上下文就会改变,因此在调用该函数之后,这个对象所属的线程上下文不能再使用。
  • 如果对象正在执行某个函数,而该函数又没有使用线程锁,那么在移动对象之后,该函数仍然会在原来的线程中执行。因此,在移动对象之前,需要确保该对象不处于执行状态。
  • 如果一个QObject对象的内部对象并没有指定父对象是这个QObject对象,且内部对象也要移动到子线程中,则需要单独操作,就像移动这个QObject对象一样。若QObject对象的内部对象指定了父对象是这个QObject对象,那么这个内部对象会自动一起移动到子线程。
  • 线程只要进行暂停休眠等待,一定会阻塞事件循环,无论是在子线程还是在主线程中,在子线程中执行任务时进行线程休眠了看似好像是顺序一个一个的执行,但其实是由于当前任务阻塞了事件循环,其他任务在排队等待被执行。所以如果有任务需要进行sleep休眠等待,千万不能在主线程中执行,而是放到子线程中去,就不会卡主界面,或者用 QEventLoop 来等待,而且不会卡主事件循环。
  • 要使工作类的任务在子线程中运行,要通过工作类的槽函数来运行才行,即在主线程中发送信号到子线程,然后子线程中运行对应的槽函数就是在子线程环境在运行。如果在主线程中直接调用工作类的一些方法,实际上还是在主线程中运行,并没有到子线程运行。这一切都原因是因为:通过moveToThread转移到子线程,子线程启动后其实是启动了子线程的事件循环,所以要使任务在子线程中运行,就要使任务在子线程的事件循环中触发,即通过信号与槽函数的方式。

 

 

moveToThread时QObject对象中内含子对象的几种情况

情况一:内含子对象,但并未指定父对象

查看代码
 class Son:public QObject{
    Q_OBJECT
public:
    Son(QObject* parent = nullptr):QObject(parent){
        QObject::connect(this, &Son::sig_son, this, &Son::slot_son);
    }
signals:
    void sig_son();
public slots:
    void slot_son(){
        qDebug()<<"slot_son"<<QThread::currentThreadId();
    }
private:
};

class Father:public QObject{
    Q_OBJECT
public:
    Father(QObject* parent = nullptr):QObject(parent){
        QObject::connect(this, &Father::sig_father, this, &Father::slot_father);
    }
signals:
    void sig_father();
public slots:
    void slot_father(){
        emit s.sig_son();
        qDebug()<<"slot_father"<<QThread::currentThreadId();
    }
public:
    Son s; //内含的对象,但初始化的时候并未指定Father为Son的父对象
};

...构造函数中...
thd = new QThread();
f = new Father();
f->moveToThread(thd); //Father类对象移动到子线程中
thd->start();
...

void MainWindow::on_pushButton_clicked()
{
    qDebug()<<"main pushbutton:"<<QThread::currentThreadId();
    emit f->sig_father();
}

输出:
main pushbutton: 0x31d0
slot_father 0xe20
slot_son 0x31d0

 从输出可以看到,内建对象s其实还在主线程中,并未在子线程里,因为Father对象移动到子线程的时候是将本对象以及其子对象一起移动到子线程中,但是子对象Son并没有指定父对象为Father,所以Father其实没有子对象,所以其实只有Father对象的线程上下文切换到了子线程里,而Son对象也只是个普通内建对象。注意:也只是在Father对象的槽函数中发送Son内建对象的信号,而Son内建对象的槽函数响应并不是在子线程而已。实际上在Father对象的槽函数直接调用Son内建对象的槽函数或者普通函数会发现其实还是在子线程中的,那是因为整个Father对象的槽函数就是在子线程中运行,那么子线程中做的任意事当然也在子线程中。但通过信号和槽则不一样,因为信号是能够夸线程传送的,所以看Son内建对象的信号所对应的槽函数是在哪个线程就知道该Son内建对象的线程上下文是否也转移到了子线程中。

 

情况二:内含子对象,未指定父对象,但其子对象也一起moveToThread到子线程中了

查看代码
 class Son:public QObject{
    Q_OBJECT
public:
    Son(QObject* parent = nullptr):QObject(parent){
        QObject::connect(this, &Son::sig_son, this, &Son::slot_son);
    }
signals:
    void sig_son();
public slots:
    void slot_son(){
        qDebug()<<"slot_son"<<QThread::currentThreadId();
    }
private:
};

class Father:public QObject{
    Q_OBJECT
public:
    Father(QObject* parent = nullptr):QObject(parent){
        QObject::connect(this, &Father::sig_father, this, &Father::slot_father);
    }
signals:
    void sig_father();
public slots:
    void slot_father(){
        emit s.sig_son();
        qDebug()<<"slot_father"<<QThread::currentThreadId();
    }
public:
    Son s; //内含的对象,但初始化的时候并未指定Father为Son的父对象
};

...构造函数中...
thd = new QThread();
f = new Father();
f->moveToThread(thd); //Father类对象移动到子线程中
f->s.moveToThread(thd); //Father类中的子对象也移动到子线程中
thd->start();
...

void MainWindow::on_pushButton_clicked()
{
    qDebug()<<"main pushbutton:"<<QThread::currentThreadId();
    emit f->sig_father();
}

输出:
main pushbutton2: 0x8a8c
slot_son 0x9318
slot_father 0x9318

 从输出可以看到,这时内建对象s也在子线程中了,因为它和Father类对象一起手动移动到了子线程中。

 

情况三:内含子对象,并指定父对象

查看代码
 class Son:public QObject{
    Q_OBJECT
public:
    Son(QObject* parent = nullptr):QObject(parent){
        QObject::connect(this, &Son::sig_son, this, &Son::slot_son);
    }
signals:
    void sig_son();
public slots:
    void slot_son(){
        qDebug()<<"slot_son"<<QThread::currentThreadId();
    }
private:
};

class Father:public QObject{
    Q_OBJECT
public:
	//初始化的时候让子对象s指定了父对象为本类,也就是Father类
    Father(QObject* parent = nullptr):QObject(parent),s(this){
        QObject::connect(this, &Father::sig_father, this, &Father::slot_father);
    }
signals:
    void sig_father();
public slots:
    void slot_father(){
        emit s.sig_son();
        qDebug()<<"slot_father"<<QThread::currentThreadId();
    }
public:
    Son s; //内含的对象,但初始化的时候并未指定Father为Son的父对象
};

...构造函数中...
thd = new QThread();
f = new Father();
f->moveToThread(thd); //Father类对象移动到子线程中
thd->start();
...

void MainWindow::on_pushButton_clicked()
{
    qDebug()<<"main pushbutton:"<<QThread::currentThreadId();
    emit f->sig_father();
}

输出:
main pushbutton2: 0x2f94
slot_son 0x909c
slot_father 0x909c

 可以看到,内建对象s在子线程中了,因为内建对象初始化的时候指定了父对象为Father,而Father的线程上下文移动到了子线程中,所以Father的子对象一起也移动到了子线程中。

 

 

 

 

 

标签:moveToThread,son,Qt,对象,Father,QObject,线程,注意事项,Son
From: https://www.cnblogs.com/zq8421/p/17589405.html

相关文章

  • Qt 生成应用程序(二)软件多图标与文件操作
    目录关联某种文件的默认打开方式assocftype解决方案设置文件默认图标应用软件添加多个图标综合方法嘿,各位Qt桌面应用开发的同学们(应该Qt大部分应用场景就是这个吧......
  • springboot整合mqtt 消费端
    用到的工具:EMQX,mqttx,idea工具使用都很简单,自己看看就能会。订阅端config代码:packagecom.example.demo.config;importlombok.extern.slf4j.Slf4j;importorg.eclipse.paho.client.mqttv3.*;importorg.eclipse.paho.client.mqttv3.persist.MemoryPersistence;imp......
  • QT入门学习记录01
    目录前言一、Qt安装二、创建一个Qt工程三、基类的区别和常用函数1.QWidget1.1设置窗口标题1.2设置窗口大小和显示位置1.3显示窗口1.4隐藏窗口1.5改变窗口大小1.6设置窗口的位置1.7刷新窗口2.QDialog2.1QDialog对话框总结前言做嵌入式的上位机开发需要要用到Qt的,Qt是一个......
  • 服务器安装SSL证书有哪些注意事项?
    在当今互联网环境中,安装SSL证书是保护网站和用户数据安全的重要步骤。正确地安装SSL证书可以确保网站实现加密通信,增强访问者对网站的信任度。然而,服务器安装SSL证书涉及一些注意事项,以确保顺利完成安装并提供最佳的安全性。安信证书将介绍服务器安装SSL证书的注意事项,以帮助您避免......
  • 【分布式技术专题】「架构设计方案」盘点和总结RBAC服务体系的功能设计及注意事项技术
    前言介绍权限管理是后台系统的重要组成部分,主要目的是控制不同人对资源的访问权限,以避免操作错误和隐私数据泄露等风险问题。我在公司负责权限管理,对该领域的设计很熟悉。公司采用微服务架构,因此权限系统独立于其他业务系统,包括商品中心、订单中心、用户中心、仓库系统、小程序和多......
  • 基于PyQt5实现聊天窗口
    本文仅为示例,用于参考,功能并不完整主要实现以下功能:1.重写PlainTextEdit组件实现类似微信ctrl+enter换行,enter发送信息操作2.带头像的消息气泡创建3.消息气泡的自适应大小窗口布局(详细可以通过示例代码中的.ui进行查看)代码获取地址GitHub:PyQt5-Chat-Demo百度网盘:http......
  • Redis注意事项
    Redis的键总是一个SDS字符串对象,Redis的值可以是SDS字符串对象,列表对象,哈希对象,集合对象,有序集合对象中的某一个,Redis底层实现是字典 字符串键:键为字符串对象,值为字符串对象 列表键:键为字符串对象,值为列表对象 哈希键:键为字符串对象,值为哈希......
  • qt 只允许启动一个实例
    参考:https://blog.csdn.net/bloke_come/article/details/106319236网址里列了三种方法。本来我的程序里使用了共享内存,在windows下没问题,在中标麒麟下崩溃后无法释放共享内存,导致程序无法重新启动。后来改为了文件锁。发现文件锁这段代码只能放在main()函数中,不能封装成另一个......
  • Qt发布程序
    发布程序以release模式运行程序,调试没有问题找到项目同级目录下build开头的对应的文件夹,在文件夹中打开release文件夹将文件夹内的可执行程序复制粘贴到一个单独的文件夹中开始菜单,qt文件夹下找到对应编译套件的运行终端,打开切换到放置exe程序的文件夹中,然后执行如下命令,*号......
  • pyqt的安装,使用
    1.pip安装PyQt5,在对应的python目录下安装  pip3installpyqt5-ihttps://pypi.tuna.tsinghua.edu.cn/simple--target=D:\QAXDownload\python\Lib\site-packages2.安装QtToolspip3installpyqt5-tools-ihttps://pypi.tuna.tsinghua.edu.cn/simple--target=D:\QAXDow......