在平时的编码过程中经常碰到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实现多线程操作的理解相关推荐

  1. Qt中与文件目录相关操作

    一.与文件目录操作有关操作. Qt中与文件目录相关的操作在QDir中,需加入#include <QDir>语句. QDir::drives()是列出电脑根目录下的所有目录,返回的是QFil ...

  2. 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 ...

  3. Qt 中 moveToThread

    概述 使用 QObject::moveToThread() 方法,可以把一个继承自 QObject 的对象交给一个 QThread 对象,然后再调用 QThread 的 start() 函数使其全权处 ...

  4. QT中使用GDAL多线程读取遥感图像到QImage

    GDAL 是一个很强大的可以读取很多格式 的带有GIS信息的栅格型图像.前阵子项目中需要读取遥感图像,并显示到QT界面,由于遥感图像一般很大,所以采取新开一个读图线程的方式来读取,防止界面假死.下面是 ...

  5. 对目标追踪MOSSE算法中FFT与卷积操作的理解

    目标追踪 目标追踪是现在机器视觉下面一个研究方向,主要是研究如何对视频中的一个物体进行追踪.可以用在军事领域无人机对地面目标进行自动瞄准和打击,或者是刑侦领域在大量的监控摄像数据中筛选出某个警方感兴趣 ...

  6. QT中循环显示图片和简单的显示图片

    请关注我的github https://github.com/linqiaozhou 以下实例代码不久后将会上传到我的github 这是我最近一个项目中的部分代码 //以下是简单的在QT中显示图片的代 ...

  7. Qt 笔记:Qt中的IO操作

    Qt中IO操作的处理方式 -Qt通过统一的接口简化了文件与外部设备的操作方式 -Qt中的文件被看做一种特殊的外部设备 -Qt中的文件操作与外部设备的操作相同 IO操作中的关键函数接口 -打开设备:bo ...

  8. FMDatabaseQueue 数据库多线程操作、事务处理

    SQLite数据库多线程操作: 在上面一节中已经讲过FMDB的用法了,接下来讲讲sqlite在都线程中的用法.如果应用中使用了多线程操作数据库,那么就需要使用FMDatabaseQueue来保证线程安 ...

  9. Qt中多线程moveToThread

    Qt中实现多线程的方式.1.利用继承QThread类,重写run函数实习.2.将对象使用moveToThread方法,利用信号槽实现.3.利用QtConcurrent相关QFuture实现. 以前一直 ...

最新文章

  1. 用Macbook-苹果系统写代码出现显示问题Text input context does not respond to _valueForTIProperty:
  2. OpenStack Neutron浅析(四)
  3. antd表格显示分页怎么取消_真相!Word里怎么也删不掉的文档空白页原来是这样...
  4. JQuery Tree 树形结构插件 zTree
  5. 深入理解JavaScript系列(32):设计模式之观察者模式
  6. Java运行时的数据区域
  7. Network-Monitor项目中观察者模式解析
  8. 修改MYSQL数据库表的字符集
  9. 电驴搜索服务器正在连接,电驴连接不上服务器导致无法搜索解决的方法介绍
  10. lol微信登录服务器,LOL开启微信登录功能测试 绑定微信登陆方法一览
  11. 如何成功将qlv格式转换成mp4,方法免费简单好用
  12. 哔哩哔哩HLB站缓存合并电脑版链接:https://wwa.lanzoui.com/b016vmouf版本:1.1公告:1、支持旧版(blv)合并2、新增提取音频功能3、修复合并失败的bug
  13. CKEditor5富文本编辑器在vue中的使用
  14. 求Decimal的相反数
  15. 三菱q系列plc连接电脑步骤_三菱Q系列PLC 以太网通信设置方法
  16. 一线城市上海的户口有多好?
  17. linux添加HP网络打印机,在Ubuntu 18.10系统下安装HP网络打印机的方法
  18. C++无名命名空间中定义的函数不使用造成“-Wunused-function”警告问题
  19. 白月黑羽教python之selenium:课后练习作业一
  20. 我用了九个小时给高中同学写了一款留言板

热门文章

  1. 电商分析:网站运营不得不做的用户分析
  2. CSUST OJ 1006 (hash)
  3. vue项目 mock数据
  4. MongoDB Data Models 数据结构设计
  5. 如何获取服务器的CPU,内存进行限流
  6. Windows下通过scp下载Linux服务器文件
  7. 如何用递归树求快速排序时间复杂度
  8. Python入门习题大全——最喜欢的数字
  9. linux系统将grub安装至硬盘,grub安装
  10. sql注入原理及防范方式