目录

  • 简介
  • 源码分析
    • 判断是否可以执行移动动作
    • 执行移动动作
      • 调用moveToThread_helper
      • 调用setThreadData_helper

简介

每一个QObject子类都会关联到一个具体QThread线程上,QObject有一个QThreadObject数据成员,该成员在Qobject构造时关联到具体的线程上:

class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{...QThreadData *threadData; // id of the thread that owns the object...
}QObject::QObject(QObjectPrivate &dd, QObject *parent): d_ptr(&dd)
{...d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();d->threadData->ref();...
}

可以看到,其实对象在构造时,就会与具体的线程关联起来:
1)如果有parent且parent已经关联到具体线程,则会直接关联到parent所在的线程;
2)否则,关联到当前线程。

而moveToThread用于将一个QObject子类对象移到另一个线程,常见于多线程编程中。

源码分析

movetoThread主要分两部分:

  1. 判断是否可以执行移动动作
    1.1 已经位于目标线程不用移动
    1.2 有parent的对象不能移动
    1.3 UI控件不能移动
  2. 执行移动动作
    2.1 发送threadChange事件
    2.2 处理消息队列中消息receiver为自己的消息
    2.3 处理自己的connection
    2.4 修改threadData

判断是否可以执行移动动作

  1. 已经位于目标线程不用移动

     if (d->threadData->thread.loadAcquire() == targetThread) {// object is already in this threadreturn;
    }
    

    threadData的thread其实是指向一个QThread对象;

    class QThreadData
    {...QAtomicPointer<QThread> thread;...
    }
    

    如果自己已经在目标线程了,那当然啥都不用做了。

  2. 有parent的对象不能移动
    if (d->parent != 0) {qWarning("QObject::moveToThread: Cannot move objects with a parent");return;
    }
    

    这个就是这样写的,我暂时不知道为啥,也许是因为处理起来很麻烦。

  3. UI控件不能移动
    if (d->isWidget) {qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");return;
    }
    

    我们知道QT是不能在非UI线程创建控件的,所以这个也很好理解,不能将控件移到非UI线程。
    三个判断到这里就结束了。下面看看具体的移动动作涉及的几个方面:

执行移动动作

调用moveToThread_helper

  1. 发送threadChange事件
    前面判断完后,会调用

    // prepare to move
    d->moveToThread_helper();
    

    moveToThread_helper很简单,就是发送个事件,然后对子对象递归调用。

    void QObjectPrivate::moveToThread_helper()
    {Q_Q(QObject);QEvent e(QEvent::ThreadChange);QCoreApplication::sendEvent(q, &e);for (int i = 0; i < children.size(); ++i) {QObject *child = children.at(i);child->d_func()->moveToThread_helper();}
    }
    

    发送了ThreadChange事件给自己。
    QObject中对此事件的处理就是释放定时器:

        case QEvent::ThreadChange: {Q_D(QObject);QThreadData *threadData = d->threadData;QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed();if (eventDispatcher) {QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);if (!timers.isEmpty()) {// do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).eventDispatcher->unregisterTimers(this);QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers))));}}break;
    }
    

    由于处理的是该对象已注册的定时器,所以在这一步用到的threadData得是转移前的线程,所以首先就是发这个消息。

调用setThreadData_helper

  1. 处理消息队列中消息receiver为自己的消息
    调用moveToThread_helper完后,之后的几个步骤都是setThreadData_helper调用里的:

       if (!targetData)targetData = new QThreadData(0);currentData->ref();// move the objectd_func()->setThreadData_helper(currentData, targetData);
    
    int eventsMoved = 0;
    for (int i = 0; i < currentData->postEventList.size(); ++i) {const QPostEvent &pe = currentData->postEventList.at(i);if (!pe.event)continue;if (pe.receiver == q) {// move this post event to the targetListtargetData->postEventList.addEvent(pe);const_cast<QPostEvent &>(pe).event = 0;++eventsMoved;}
    }
    if (eventsMoved > 0 && targetData->hasEventDispatcher()) {targetData->canWait = false;targetData->eventDispatcher.loadRelaxed()->wakeUp();
    }
    

    这个可以分成2小步:
    1)遍历对象所处当前线程中所有事件,将receiver为自己的事件全部移到(不是复制)新线程的消息队列里。
    2)如果真的有事件被移动,则尝试对目标线程调用wakeUp,告诉线程可以起来工作了。

  2. 处理自己的connection
       ConnectionData *cd = connections.loadRelaxed();
    if (cd) {if (cd->currentSender) {cd->currentSender->receiverDeleted();cd->currentSender = nullptr;}// adjust the receiverThreadId values in the Connectionsif (cd) {auto *c = cd->senders;while (c) {QObject *r = c->receiver.loadRelaxed();if (r) {Q_ASSERT(r == q);targetData->ref();QThreadData *old = c->receiverThreadData.loadRelaxed();if (old)old->deref();c->receiverThreadData.storeRelaxed(targetData);}c = c->next;}}}
    

    这块儿代码比较长,暂时还没想明白,后面想明白了再来更新,有大佬知道的话也可以说下。

  3. 修改threadData
    targetData->ref();
    threadData->deref();
    threadData = targetData;
    

    全部事情都做完后,用到threadData的地方都处理完了,就可以更新threadData了,ref增加引用,deref解引用。
    上面执行完后,同moveToThread_helper一样,也会遍历子对象递归调用setThreadData_helper。

QT moveToThread解析相关推荐

  1. 一个基于QT的解析interproscan结果的C++成员函数

    结构域预测软件interproscan提供多种输出格式,出于后期分析的需要,选用了gff3格式.我比较喜欢结合数据库进行分析,所以先要把数据导入数据库. 我之前用QT写好了界面,所以只要在菜单里添加一 ...

  2. Qt:解析xml文件

    XML 什么事XML文件 XML,可扩展标记语言(Extensible Markup Language),是一种标记语言.一般用 于数据存储.配置文件存储,(Qt的ui文件就是xml)也可以跨平台跨语 ...

  3. QT——JSON解析

    一些相关知识的链接: 有关JOSN相关的基础知识:JSON基础 有关QT中JSON的操作:QT中JSON的操作 一个在线工具,可以进行JSON格式化分析的网址:JSON格式化 如何从网络中轻松得到JS ...

  4. QT Json解析方法

    QT 与WEB的交互,大部分就是客户端与网页的交互,有时,我们需要得到服务器返回的具体的数据,就通过客户端与网页交互的过程中拿到所需要的参数,在我的另一篇博文中有讲到过http://blog.csdn ...

  5. qt中解析json字符串的时候出现错误missingNameSeperator

    概述 当解析json字符串,编译代码的时候没有问题,但是当程序调式运行到解析json字符串的时候,即这句: QJsonParseError parseError;QJsonDocument doc = ...

  6. Qt JSON解析生成笔记

    对于这样一段json {"name": "布衣食","gender": "Male","age": ...

  7. QT无法解析的外部符号问题

    moc_widget.obj:-1: error: LNK2019: 无法解析的外部符号 "private: void __thiscall Widget::on_pushButton_6_ ...

  8. QT moveToThread线程理解

    一.moveToThread创建开启线程步骤: (1)创建继承自QObject类,实现槽函数. (2)将QObject类通过moveToThread方法移到QThread线程中,使QObject类依附 ...

  9. qt moveToThread错误分析

    概念:movetothread的意思就是把某个东西移动到线程里,然后通过信号与槽的方式实现调用.但是使用movetothread时,必须是继承QObject类的类. 错误情况:在使用movetothr ...

最新文章

  1. 使用CNN做文本分类——将图像2维卷积换成1维
  2. 相机模拟激光雷达 建图
  3. excel转kml工具_CAD+Excel还能这样玩?你用对了嘛!
  4. 机器学习如何计算特征的重要性_机器学习之特征缩放
  5. php进销存 手机版_酒水批发用传统本地化部署进销存与云进销存手机版的区别!...
  6. cookie 的使用
  7. SqlConnection的open打开后没关闭的后果
  8. android 代码设置alignleft,如何动态的设置Relative Layout中按钮的layout_align_parent_right属性...
  9. python web flask开发框架_零基础入门python web框架Flask开发
  10. DCMTK:类DcmUniqueIdentifier的测试程序
  11. layui的checkbox示例
  12. 《信息安全系统设计基础》 实验五
  13. HTTP协议学习随笔
  14. Tips--Altium Designer 安装时出现Account log in
  15. nodejs 读取excel文件,并去重
  16. Python-OpenCV基本操作
  17. 工作缺点和不足及措施_个人工作存在的不足和改进措施_个人工作存在问题和整改方案...
  18. Office Tool Plus 下载使用 365 2021/2019等版本
  19. 计算机跳转到用户选择,win7系统开机要选择用户才能进入系统怎么办
  20. 安卓修改电池容量教程_安卓手机用re管理器修改电池信息增加待机时间

热门文章

  1. 千锋网络安全1-3days
  2. 阿里云企业邮箱购买流程
  3. 关于无状态服务(stateless service) 有状态服务(stateful service),指一篇文章就搞明白
  4. ssh: connect to host xxx.xxx.xxx.xxx port 22: Connection refused
  5. TextView逐渐加载效果
  6. 几大ERP软件实施方法与过程 (转)
  7. CSUST - 2021 组队选拔赛
  8. 计算机如何寻址大学计算机,寻址-天津大学计算机科学与技术学院.ppt
  9. 17 | 从后端到前端:微服务后,前端如何设计?
  10. PTA-统计大写辅音字母(详细)