简单说说对QT中moveToThread实现多线程操作的理解
在平时的编码过程中经常碰到QT的多线程问题,也大量接触了QT中的两种主流多线程写法,一种是继承QThread类并重载run函数,在run函数中写一个状态机或者计时器来实现对线程运作;一种是通过moveToThread的方式实现事件托管从而实现线程运作。前者虽然是传统写法但是弊病较多,这里主要针对后者来进行说明与理解。
1.常见情况分析:该方法在进行代码编写时的常见情况如下:
class tempActive
{public:tempActive(){...a = new A;m_thread = new QThread;a->moveToThread(m_thread);...]
private:A* a;QThread* m_thread;...
};
上述代码中,tempActive为某一工作相关类,A为某一自定义类或预定义类,其实例a与实例m_thread往往在tempActive的构造函数中实现,并在其中将a实例moveToThread至m_thread线程之中。这里需要着重理解一点,并非是将a实例相关的所有的工作“移动”到了m_thread线程,而是将所有a实例相关的事件托管到m_thread线程执行。换句话说,就是通过信号槽connect或者invokeMethod触发a实例中槽函数产生的事件,将会被放置到m_thread线程中执行,从而实现了多线程工作。也就是说,此时通过a->show()等方式调用直接调用a实例中的函数,无法实现多线程功能。
2.实验验证:为了验证以及加强理解上述结论,在这里做一个简单的实验如下:
class A : public QObject
{Q_OBJECT
public:public slots:void show(){qDebug() << "show:" << QThread::currentThreadId();}
};class MainWindow : public QObject
{Q_OBJECTpublic:explicit MainWindow(QObject *parent = 0);~MainWindow();
public slots:void mainShow() {qDebug() << "mainShow:" << QThread::currentThreadId();}
signals:void s_test();
private:A* a;QTimer* m_timer;QThread* m_thread;
};MainWindow::MainWindow(QObject *parent) :QObject(parent)
{a = new A;m_thread = new QThread;m_timer = new QTimer;m_timer->setInterval(500);m_thread->start();qDebug() << "A before move:" << QThread::currentThreadId(); //A before move: 0x2a0ca->show(); //show: 0x2a0cQMetaObject::invokeMethod(a,"show",Qt::DirectConnection); //show: 0x2a0cQObject::connect(this,SIGNAL(s_test()),a,SLOT(show()),Qt::DirectConnection);emit s_test(); //show: 0x2a0cQObject::disconnect(this,SIGNAL(s_test()),a,SLOT(show()));a->moveToThread(m_thread);qDebug() << "A after move:" << QThread::currentThreadId(); //A after move: 0x2a0ca->show(); //show: 0x2a0cQMetaObject::invokeMethod(a,"show",Qt::BlockingQueuedConnection); //show: 0x3a9cQObject::connect(this,SIGNAL(s_test()),a,SLOT(show()),Qt::BlockingQueuedConnection);emit s_test(); //show: 0x3a9cQObject::disconnect(this,SIGNAL(s_test()),a,SLOT(show()));qDebug() << "m_timer before move:" << QThread::currentThreadId(); //m_timer before move: 0x2a0cm_timer->moveToThread(m_thread);this->moveToThread(m_thread);qDebug() << "m_timer after move:" << QThread::currentThreadId(); //m_timer after move: 0x2a0cQObject::connect(m_timer,SIGNAL(timeout()),this,SLOT(mainShow()),Qt::DirectConnection); QMetaObject::invokeMethod(m_timer,"start",Qt::BlockingQueuedConnection); //mainShow: 0x3a9c
}
在上述函数中,通过MainWindow的构造函数进行实验,这里分步分析下:
- 当实例a还未移动至m_thread之前,其所有的相关操作均处于当其被new时所处的线程,由于MainWindow在主线程中被构造,所以此时实例a位于主线程0x2a0c。此时通过任何方式,无论直接调用a_>show(),抑或是通过信号槽和invokeMethod调用show(),最终触发时show()必然在主线程0x2a0c之中。
- 当实例a被移动至m_thread之后,其相关事件被托管,但是该实例本身仍位于主线程0x2a0c中,但是通过信号槽和invokeMethod调用show()时发生在m_thread线程0x3a9c之中。
- 当m_timer与this被移动至m_thread之后,其所处线程仍不变为主线程0x2a0c,此时m_timer与this为同一线程,所以信号槽连接时使用DirectConnection,并且需要通过invokeMethod方式来调用m_timer的start方可在主线程0x2a0c中调用m_thread中的m_timer.start()函数(不然会报QTimer无法在另外线程打开的错误,这其实是QT对于QTimer机制的一种保护,有兴趣的读者可以研究下这里将QTimer进行moveToThread之后对于线程的限制保护机制),最后执行的mainShow()函数也确确实实的都在m_thread的线程0x3a9c中执行。
3.结论总结
- moveToThread方式并非“无脑”移动,是一种事件的托管;
- 事件的产生只能通过信号槽或invokeMethod方式来实现,其他的方式(如直接调用)将会导致所调用函数在进行直接调用的线程之中执行,而非moveToThread之后的线程。
- 由于QTimer操作对于线程的限制性,需要保证执行QTimer的相关函数时,如果想采用直接调用则需保证调用函数线程与QTimer所处线程一致,否则就必须使用信号槽或invokeMethod的方式来调用QTimer相关的函数。
- “QT官方文档moveToThread的Warning”:moveToThread往往只能将当前对象从其当前所处线程“推”到某一线程中去(比如1中的常见情况,即在构造函数中将实例a从构建tempActive的线程“推”到了m_thread线程中),不能将当前对象从某线程中“拉”到该线程(即无法在实例a不处在的线程中执行对实例a的moveToThread操作,因为无法在另一线程控制a的自身线程)
简单说说对QT中moveToThread实现多线程操作的理解相关推荐
- Qt中与文件目录相关操作
一.与文件目录操作有关操作. Qt中与文件目录相关的操作在QDir中,需加入#include <QDir>语句. QDir::drives()是列出电脑根目录下的所有目录,返回的是QFil ...
- 4.关于QT中的QFile文件操作,QBuffer,Label上添加QPixmap,QByteArray和QString之间的区别,QTextStream和QDataStream的区别,QT内存映射(
新建项目13IO 13IO.pro HEADERS += \ MyWidget.h SOURCES += \ MyWidget.cpp QT += gui widgets network CON ...
- Qt 中 moveToThread
概述 使用 QObject::moveToThread() 方法,可以把一个继承自 QObject 的对象交给一个 QThread 对象,然后再调用 QThread 的 start() 函数使其全权处 ...
- QT中使用GDAL多线程读取遥感图像到QImage
GDAL 是一个很强大的可以读取很多格式 的带有GIS信息的栅格型图像.前阵子项目中需要读取遥感图像,并显示到QT界面,由于遥感图像一般很大,所以采取新开一个读图线程的方式来读取,防止界面假死.下面是 ...
- 对目标追踪MOSSE算法中FFT与卷积操作的理解
目标追踪 目标追踪是现在机器视觉下面一个研究方向,主要是研究如何对视频中的一个物体进行追踪.可以用在军事领域无人机对地面目标进行自动瞄准和打击,或者是刑侦领域在大量的监控摄像数据中筛选出某个警方感兴趣 ...
- QT中循环显示图片和简单的显示图片
请关注我的github https://github.com/linqiaozhou 以下实例代码不久后将会上传到我的github 这是我最近一个项目中的部分代码 //以下是简单的在QT中显示图片的代 ...
- Qt 笔记:Qt中的IO操作
Qt中IO操作的处理方式 -Qt通过统一的接口简化了文件与外部设备的操作方式 -Qt中的文件被看做一种特殊的外部设备 -Qt中的文件操作与外部设备的操作相同 IO操作中的关键函数接口 -打开设备:bo ...
- FMDatabaseQueue 数据库多线程操作、事务处理
SQLite数据库多线程操作: 在上面一节中已经讲过FMDB的用法了,接下来讲讲sqlite在都线程中的用法.如果应用中使用了多线程操作数据库,那么就需要使用FMDatabaseQueue来保证线程安 ...
- Qt中多线程moveToThread
Qt中实现多线程的方式.1.利用继承QThread类,重写run函数实习.2.将对象使用moveToThread方法,利用信号槽实现.3.利用QtConcurrent相关QFuture实现. 以前一直 ...
最新文章
- 用Macbook-苹果系统写代码出现显示问题Text input context does not respond to _valueForTIProperty:
- OpenStack Neutron浅析(四)
- antd表格显示分页怎么取消_真相!Word里怎么也删不掉的文档空白页原来是这样...
- JQuery Tree 树形结构插件 zTree
- 深入理解JavaScript系列(32):设计模式之观察者模式
- Java运行时的数据区域
- Network-Monitor项目中观察者模式解析
- 修改MYSQL数据库表的字符集
- 电驴搜索服务器正在连接,电驴连接不上服务器导致无法搜索解决的方法介绍
- lol微信登录服务器,LOL开启微信登录功能测试 绑定微信登陆方法一览
- 如何成功将qlv格式转换成mp4,方法免费简单好用
- 哔哩哔哩HLB站缓存合并电脑版链接:https://wwa.lanzoui.com/b016vmouf版本:1.1公告:1、支持旧版(blv)合并2、新增提取音频功能3、修复合并失败的bug
- CKEditor5富文本编辑器在vue中的使用
- 求Decimal的相反数
- 三菱q系列plc连接电脑步骤_三菱Q系列PLC 以太网通信设置方法
- 一线城市上海的户口有多好?
- linux添加HP网络打印机,在Ubuntu 18.10系统下安装HP网络打印机的方法
- C++无名命名空间中定义的函数不使用造成“-Wunused-function”警告问题
- 白月黑羽教python之selenium:课后练习作业一
- 我用了九个小时给高中同学写了一款留言板