QT moveToThread解析

2023-10-27

简介

每一个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 thread
        return;
    }
    
    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 object
        d_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 targetList
            targetData->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 Connections
        if (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。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT moveToThread解析 的相关文章

  • Qt Creator 2.8.1 Qt 5.1.1 Qt Designer Linux 显示新窗体

    我是 Qt 的初学者 所以希望这是一个容易回答的问题 我有相当多的 C 经验 这部分不是问题 我的应用程序的目的是进行代码生成 最初是为类制作头文件和实现文件 我非常喜欢 Code Blocks 上的类向导 但我认为我可以做更多的事情 我有
  • 使用嵌入qt的mysql?

    我正在尝试使用嵌入 QT 的 mysql 我已经有一个与 mysqld 链接的 Qt mysql 插件 该插件可以很好地加载嵌入式数据库 但 QT 没有简单的方法来设置 dataDir 等嵌入式选项 我在这里看到 http doc qt i
  • 面向 Delphi 开发人员的 Qt

    有人知道为 Delphi C Builder VCL 开发人员解释 Qt 的书籍或教程吗 对于具有该背景的开发人员来说 学习 Qt 的最佳方法是什么 我对如何使用 Qt 完成我知道如何在 Delphi 中完成的事情特别感兴趣 例如 Qt 相
  • Qt 图表和数据可视化小部件

    我已经安装了 Qt 5 7 来尝试 Qt 图表和 Qt 数据可视化 但我在 Qt Designer 和 Qt Creator 中都找不到新的小部件 有什么建议我应该做什么才能让新的小部件出现在设计器中 我今天遇到了完全相同的问题 默认情况下
  • QObject多重继承

    我正在尝试在 C Qt 类中使用 mix 来提供一大堆具有通用接口的小部件 该接口是以这样的方式定义的 如果它被定义为其他小部件类的基类 那么小部件本身将具有这些信号 class SignalInterface public QObject
  • 从 Qt 更改屏幕分辨率?

    我想更改屏幕分辨率 然后使用一个 ActiveX 控件 Flash 播放器 进入全屏 显然 仅适用于 Windows 的解决方案就可以了 有 Qt api 吗 还是我需要深入研究 winapi 如果是这样 我该在哪里查找 关键字 谢谢您的帮
  • 如何在不同的QT线程中创建一个窗口?

    我有一个应用程序 其中每个线程 主线程除外 都需要创建自己的窗口 我尝试创建一个线程然后调用this gt exec in the run功能 然而 在我接到那个电话之前我就收到了一个错误 ASSERT failure in QWidget
  • QFileDialog 作为 TableView 的编辑器:如何获取结果?

    我正在使用一个QFileDialog作为某些专栏的编辑QTableView 这基本上有效 对一些焦点问题取模 请参阅here https stackoverflow com questions 22854242 qfiledialog as
  • 构建qt程序时未定义的符号:找不到qt_version_tag

    我正在学习Qt5 6 我正在使用 Ubuntu 14 4 当我链接我的程序时 出现以下错误 undefined reference to qt version tag 在 CMakeLists txt 中 link libraries Qt
  • Qt 和 MOC 的困境与简单的制作

    我想这更像是一个 GNU Make 问题 而不是 Qt 和 moc 但这里是 我有一个包含多个目录Q OBJECTS 我有一些简单的代码 它收集所有这些 例如 MOCS shell grep l Q OBJECT HEADERS Assum
  • 在 QML 中控制纹理 3D 对象的不透明度

    我对 QML 中的 Qt 3D 有点陌生 我正在尝试控制 Qt 3D 的不透明度textured3D 对象 我正在使用简单qml3d https github com tripolskypetr simpleqml3d测试项目来做到这一点
  • 仅当从 Qt 连接时网页返回 HTTP 406 错误

    我有一个测试页面设置http mlecturedownload com test qt php http mlecturedownload com test qt php有以下代码
  • Qt 码头调整大小事件

    有没有办法在 Qt 中捕捉码头的调整大小事件 我需要能够检测到扩展坞何时调整大小 而不仅仅是其位置或 功能 发生变化时 看起来 QDockWidget 没有 调整大小 信号 如果您不希望子类化以仅获得调整大小事件控件 您可以安装事件过滤器
  • Qmake 不支持源目录下的构建目录

    我创建了一个可以在 OS X 上编译和运行的应用程序 我现在想开始让它在 Windows 上运行 首先 我将项目复制到 Windows 机器上并尝试编译 但收到此错误 警告 Qmake不支持源目录下的构建目录 有任何想法吗 将影子构建目录设
  • 使用 QTestLib 时抑制 qDebug

    我正在向 Qt 中的项目添加单元测试 并希望使用 QTestLib 我已经设置了测试并且它们运行良好 问题是在项目中我们重写了 qDebug 以输出到我们自己的日志文件 这在运行应用程序时效果很好 问题是当我测试类时 它有时会开始记录 然后
  • 如何使用样式表删除 QWizard 中的水平线?

    我正在研究一个样式表QWizard我想删除按钮上方的水平线 我已经发布了一个最小的例子here https stackoverflow com q 52538669 8570451 这个问题是由scopchanov从最小的例子中解决的 但是
  • 使用 QPrinter 打印第 x 页(共 y 页)

    我使用 qt 从 html 代码生成 pdf 文件 QTextDocument document new QTextDocument document gt setHtml htmlContent QPrinter printer QPri
  • 使用信号和槽更新指针

    我对 Qt 很陌生 请帮我解决这个问题 我正在使用线程在后台执行密集操作 同时我想更新 UI 所以我使用 SIGNALS 和 SLOTS 为了更新 UI 我发出一个信号并更新 UI 让我们考虑下面的示例代码 struct sample QS
  • 如何从 ffmpeg 中打开的文件获取流信息?

    我正在尝试使用 ffmpeg 读取视频文件 我有与其旧版本相对应的工作代码 并开始尝试升级到最新的构建版本 将所有这些已弃用的函数替换为其实际的类似函数 但是我遇到了问题 似乎没有检索到任何流 并且视频负载停止在轨道中 这是我正在使用的代码
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图

随机推荐

  • Android平台的兼容相关概念详细整理

    设置应用的支持版本 通过我们会在文件夹下 指定下面属性 来表示我们应用所支持的Android版本
  • Summary of OpenGL ES 3.0 Programming Guide

    零 OpenGLES简介 OpenGLES 是用于嵌入式系统的一个3D图形编程接口规范 支持IOS Android Linux Windows WebGL 一 渲染管线 1 VertexBufferArrayObject 2 VertexS
  • Python 高级变量类型,函数进阶 day05

    高级变量类型 目标 列表 元组 字典 字符串 公共方法 变量高级 知识点回顾 Python 中数据类型可以分为 数字型 和 非数字型 数字型 整型 int 浮点型 float 布尔型 bool 真 True 非 0 数 非零即真 假 Fal
  • 命名空间system中不存在data_我理解中的Unity DOTS

    我是第一次接触到 ECS 并不是使用老手 以下仅仅是我通过浏览相关文章和实现相关 Demo 而写出的一篇分享初见文 如有错误 请您指出 谢谢 因为本身就是忠实的 Overwatch 玩家 所以天然的对其应用的 ECS 架构有所兴趣 再加上最
  • 流文件保存到本地的两种方法

    1 FileStream fs new FileStream path FileMode Create fs Write ImageBuff 0 ImageBuff Length fs Close 注释 ImageBuff 图片流 byte
  • HEX(Xilinx MCS)文件格式详解

    文章目录 自己定义个文件格式 HEX文件格式详解 HEX 文件是指以hex为后缀 采用Intel HEX编码规则的文件 可以直接使用文本编辑工具打开 通常用来对微控制器或ROM进行编程 本质上都是对存储器编程 其中包含了每个地址对应的数据
  • QT connect()连接函数的一点说明

    QObject connect中的signal和slot函数一定要有参数类型 但是 不可以有参数名 指定信号和方法时 必须使用SIGNAL 和SLOT 宏 例如 关联信号和槽函数 connect configureWindow SIGNAL
  • MySQL索引失效场景实例演示

    文章目录 一 环境信息 二 表 数据准备 三 复合索引的失效情况 最左前缀法则 or查询 四 单列索引的失效情况 like模糊查询使用前通配符 索引列上使用函数 字符串索引没加引号 使用 lt gt gt lt is null 或 is n
  • Jenkins安装与配置

    什么是CICD CI CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法 CI CD 的核心概念是持续集成 持续交付和持续部署 作为一个面向开发和运营团队的解决方案 CI CD 主要针对在集成新代码时所引发的问题 亦称 集成
  • Ubuntu16.04下基于Docker的Caffe-GPU版本环境搭建总结

    Caffe的GPU环境搭建在docker支持下并不困难 但是过程比较杂 所需依赖如下 本文会说明安装方法 帮助大家少走弯路 GNU Linux x86 64 with kernel version gt 3 10 Docker gt 1 9
  • 这是一个更新版本服务代码python

    import win32serviceutil import win32service import win32event import os import logging import inspect import time from u
  • 【计算机网络】实验报告三:Cisco Packet Tracer 实验

    Cisco Packet Tracer 实验 1 直接连接两台 PC 构建 LAN 2 用交换机构建 LAN 3 交换机接口地址列表 4 生成树协议 Spanning Tree Protocol 5 路由器配置初步 6 静态路由 7 动态路
  • 1045 快速排序

    著名的快速排序算法里有一个经典的划分过程 我们通常采用某种方法取一个元素作为主元 通过交换 把比主元小的元素放到它的左边 比主元大的元素放到它的右边 给定划分后的 N 个互不相同的正整数的排列 请问有多少个元素可能是划分前选取的主元 例如给
  • typora高亮_用Typora实现写作排版一体化

    用Typora实现写作排版一体化 一般的推文制作过程 大概都是在本地word写好文案以后 再导入到第三方的推文编辑器中进行排版 最后再黏贴到微信的图文素材编辑器里生成图文发布 经常看到许多公众号文章充满花里胡哨的元素 大概都是因为使用了推文
  • SpringBoot整合Elasticsearch(最新最全,高效安装到使用)

    文章目录 一 安装Elasticsearch相关插件 1 选择版本 2 安装Elasticsearch 3 安装node 4 安装grunt 5 安装es head插件 6 安装kibana 7 安装ik分词器 二 整合SpringBoot
  • 从零开始学编程——DOS命令

    一 DOS操作系统 DOS 英文 Disk Operating System 是一款由微软早期推出的磁盘操作系统 可以通过一系列dos命令直接对硬盘文件进行增删改查 DOS和windiws的最大不同之处在于它是一个字符式操作系统 所有的操作
  • Oracle 表空间查询与操作方法

    一 查询篇 1 查询oracle表空间的使用情况 select b file id 文件ID b tablespace name 表空间 b file name 物理文件名 b bytes 总字节数 b bytes sum nvl a by
  • 自动化办公更简单了:新版python-office,有哪些更新?

    职场经验谈 大家好 这里是程序员晚枫 小破站 小红薯都叫这个名 去年4月开源了一个Python自动化办公项目 python office GitHub和Gitee都能看到 1行代码实现复杂的自动化办公任务 帮助不懂代码的小白 快速使用Pyt
  • Unity鼠标控制物体的旋转、移动、缩放等

    这个是控制相机 44条消息 unity 相机 旋转缩放查看 物体或地图 unity旋转查看物体 野区捕龙为宠的博客 CSDN博客 下面的是控制物体本身 知识点 Input GetMouseButton 0 获取鼠标输入 参数为一个int值
  • QT moveToThread解析

    目录 简介 源码分析 判断是否可以执行移动动作 执行移动动作 调用moveToThread helper 调用setThreadData helper 简介 每一个QObject子类都会关联到一个具体QThread线程上 QObject有一