【QT】Qt多线程中的信号与槽

2023-11-01

【QT】Qt多线程中的信号与槽

1. Qt对象的依附性和事务循环

QThread继承自QObject,自然拥有发射信号/定义槽函数的能力。QThread默认声明了以下几个关键信号(信号只能声明不能定义):

  • (1) 线程开始运行时发射的信号
void started()
  • (2)线程完成运行时发射的信号
void finished()
  • (3) 线程被异常终止时发射的信号
void terminated()

多线程中的信号与槽之间的关系,是Qt编程中一个相对复杂的内容。先看下面例子:

//TestThread.h声明了线程类
#ifndef TESTTHREAD_H
#define TESTTHREAD_H

#include <QThread>
#include <QObject>
#include <QDebug>

//在线程类TestThread中自定义信号和槽函数
class TestThread : public QThread
{
    Q_OBJECT

protected:
    void run(); //线程执行函数

public:
    explicit TestThread(QObject *parent = 0);

signals:
    void TestThread_Signal();   //自定义的信号

public slots:
    void TestThread_Slot();     //对应TestThread_Signal()信号的槽函数
};

#endif // TESTTHREAD_H

//TestThread.cpp
#include "TestThread.h"
#include <QDebug>

TestThread::TestThread(QObject *parent) :
    QThread(parent)
{
    //连接信号与槽
    connect(this, SIGNAL(TestThread_Signal()), this, SLOT(TestThread_Slot()));
}

//子线程执行函数
void TestThread::run()
{
    //打印新线程的ID值
    qDebug() << "void TestThread::run(): tid = " << currentThreadId();

    //发射自定义信号
    emit TestThread_Signal();
}

void TestThread::TestThread_Slot()
{
    //打印此函数运行时的所在线程的ID值
    qDebug() << "TestThread::TestThread_Slot(): tid = " << currentThreadId();
}

//main.c
#include <QtCore/QCoreApplication>
#include "TestThread.h"
#include "MyObject.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main: tid = " << QThread::currentThreadId();

    TestThread t;

    //开启线程执行函数,run()得到执行
    t.start();

    return a.exec();
}

运行:
在这里插入图片描述
上面代码的运行结果:
  槽函数执行时的所在线程和信号发送操作的所在线程并不是同一个,前者位于main线程中,后者位于子线程中。

由此可以引申两个问题:

  • (1) 二者同属于子线程类,程序运行时发送信号操作在子线程完成,对应的槽函数却是在main线程执行,究其原因,得从Qt对象的依附性说起。
      在Qt编程中,默认情况下,对象依附于创建自身的线程,例如上面代码中TestThread对象t它是在main()函数中创建的,那么t依附于主线程,而槽函数在其所依附的线程中被调用执行,因此,槽函数TestThread_Slot()是在main线程中执行

要想让TestThread_Slot()函数运行在main()创建的子线程中,可以使用moveToThread()函数更改TestThread对象所依附的线程:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main: tid = " << QThread::currentThreadId();

    TestThread t;

    t.moveToThread(&t);     //更改TestThread对象的依附性到t线程

    t.start();

    return a.exec();
}

运行:
在这里插入图片描述
需要强调,这样的做法在实际工程中并不采取。因为QThread应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的业务逻辑代码。显然,修改TestThread对象所依附的线程恰恰是打算在线程类对象中实现业务逻辑代码。

  • (2) 若要对上面的代码的运行结果做个转换:槽函数运行时所依赖的是子线程,信号发送操作是在main()线程中完成,何以实现?
//定义一个存放槽函数的类MyObject,它继承自QObject
//MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H

#include <QObject>
#include <QThread>
#include <QDebug>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject *parent = 0);

public slots:           //定义两个槽函数
    void getStarted();
    void MyObjectSlot();
};

#endif // MYOBJECT_H

//MyObject.cpp
#include "MyObject.h"

MyObject::MyObject(QObject *parent) :
    QObject(parent)
{
}

void MyObject::getStarted()
{
    qDebug() << "MyObject::getStarted(): tid = " << QThread::currentThreadId();
}

void MyObject::MyObjectSlot()
{
    qDebug() << "MyObject::MyObjectSlot(): tid = " << QThread::currentThreadId();
}

在main函数中,定义线程对象和MyObject对象,并将MyObject的依附性改为子线程
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main: tid = " << QThread::currentThreadId();

    TestThread t;
    MyObject m;

    //m.moveToThread(&t);
    QObject::connect(&t, SIGNAL(TestThread_Signal()), &m, SLOT(MyObjectSlot()));
    t.start();      //run()函数的实现体和上面的一样,会发射TestThread_Signal()信号。该信号在这里还增加连接了MyObjectSlot的槽,所以除了会触发
                    //TestThread_Slot()函数外还会触发MyObjectSlot()

    return a.exec();
}

运行:
在这里插入图片描述
 TestThread::TestThread_Slot()得到调用,但是MyObject::MyObjectSlot()却没有,这就要引入事件循环的概念。当发送信号操作所在的线程和槽函数执行时所在的线程是不同线程时,就需要事件循环的支持。如main函数中的”a.exec()”函数的调用也是开启事件循环,QThread线程类同样提供了exec()函数用于开启线程的事件循环,发送信号和槽函数位于不同的线程时,只有槽函数所在线程开启了事件循环,它才能在对应信号发射后被调用。无论事件循环是否开启,信号发送后会直接进入槽函数所依附的线程的事件队列,然而,只有开启了事件循环,对应的从函数才会在线程中得到调用。
在这里插入图片描述
从上图可知,事件循环是一个无止尽循环,事件循环结束之前,exec()函数后的语句无法得到执行。问线程如何正常结束?这就需要quit()或者载exit()函数用于结束事件循环。

在TestThread::run()函数中增加exec()函数的调用:

void TestThread::run()
{
    qDebug() << "void TestThread::run(): tid = " << currentThreadId();

    emit TestThread_Signal();

    exec(); //开启子线程的事件循环
    qDebug() << "hello";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main: tid = " << QThread::currentThreadId();

    TestThread t;
    MyObject m;


    m.moveToThread(&t);

    QObject::connect(&t, SIGNAL(TestThread_Signal()), &m, SLOT(MyObjectSlot()));

    t.start();          //开启线程执行函数
    t.wait(8 * 1000);   //休眠等待8s,确保事件队列中的槽函数得到执行后,再让子线程结束事件循环
    t.quit();

    return a.exec();
}

运行:
在这里插入图片描述
 需要注意的是,当信号的发送与对象的槽函数的执行在不同线程中时,可能会产生临界资源的竞争问题


2. 信号与槽的连接方式

信号与槽的连接函数的原型为:

bool QObject::connect (const QObject * sender, 
                        const char * signal, 
                        const QObject * receiver,
                        const char * method,
                        Qt::ConnectionType type = Qt::AutoConnection)

其中第5个参数决定信号与槽的连接方式,用于决定槽函数被调用时的相关行为。

Qt::AutoConnection              默认连接
Qt::DirectConnection            槽函数立即调用
Qt::BlockingQueuedConnection    同步调用
Qt::QueuedConnection            异步调用
Qt::UniqueConnection            单一连接
  • (1) Qt::DirectConnection(立即调用)
      直接在发送信号的线程中调用槽函数(发送信号和槽函数位于同一线程),等价于槽函数的实时调用。

  • (2) Qt::QueuedConnection(异步调用)
      信号发送至目标线程的事件队列(发送信号和槽函数位于不同线程),交由目标线程处理,当前线程继续向下执行。

  • (3) Qt::BlockingQueuedConnection(同步调用)
      信号发送至目标线程的事件队列,由牧宝想线程处理。当前线程阻塞等待槽函数的返回,之后向下执行。

  • (4) Qt::AutoConnection(默认连接)
      当发送信号线程=槽函数线程时,效果等价于Qt::DirectConnection;
      当发送信号线程!=槽函数线程时,效果等价于Qt::QueuedConnection。
      Qt::AutoConnection是connect()函数第5个参数的默认值,也是实际开发中字常用的连接方式。

    1. Qt::UniqueConnection(单一连接)
        功能和AutoConnection相同,同样能自动确定连接类型,但是加了限制:同一个信号和同一个槽函数之间只能有一个连接。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【QT】Qt多线程中的信号与槽 的相关文章

  • 删除 QComboBox“下拉”动画

    我正在使用 Qt 4 8 并且想在单击 QComboBox 时摆脱 下拉 动画 我也想稍微移动一下 到目前为止 我一直在考虑重新实现 showPopup 和 hidePopup 但不知道如何使其工作 此外 每次我尝试使用 CSS 进行移动或
  • 检查目录是否为空

    我正在尝试检查目录是否为空 MainWindow MainWindow QWidget parent QMainWindow parent ui new Ui MainWindow ui gt setupUi this QDir Dir h
  • Windows 消息循环而不是 QApplication::exec() / QApplication::processEvents()

    我是否想念任何一个Qt如果我替换功能QApplication exec 使用标准 Windows 消息循环实现 这应该可以澄清我的意思 运行事件处理的常用 Qt 方式 int main int argc char argv QApplica
  • setContextProperty 和对象的 setProperty 之间的区别

    我现在真的很困惑 有什么区别 QQmlApplicationEngine engine engine rootContext setContextProperty myObject userData and object gt setPro
  • 如何获取 QIcon 的文件/资源​​路径

    假设我做了这样的事情 QIcon myIcon resources icon ico 我稍后如何确定该图标的路径 例如 QString path myIcon getPath 问题是 没有getPath 会员 我找不到类似的东西 但肯定有办
  • Qt 支持在 QIcon 中为 SVG 着色

    看来 Qt 不支持 SVG 中路径标签上的描边 填充选项
  • MAC 上的 QT/C++ - 未设置应用程序图标

    我正在努力解决的奇怪问题 在与我的 pro QT 项目文件相同的文件夹中 我有一个 Resources myIcon png 我试图将其设置为我构建的应用程序的图标 在 OSX 上运行 我阅读了文档 它建议在 pro 文件中添加 ICON
  • 为什么下面的重叠比较总是评估为 true

    我不明白为什么以下代码有警告 指出重叠比较始终评估为真 接下来的语句永远不会被执行 QVariant MainModel data const QModelIndex index int role const if index isVali
  • 当 QML 对象不可见时防止 QML 属性绑定?

    我正在开发一个具有大量属性绑定的 QML 应用程序 数百个对象被跟踪并以不同的形式显示 例如 Qt3D QCanvas 当我在应用程序的单独页面上时 x y 位置和相对大小的属性绑定仍在发生 我怎样才能阻止他们 我知道我可以根据属性是否可视
  • Qt/c++ 随机字符串生成[重复]

    这个问题在这里已经有答案了 我正在创建一个应用程序 需要生成多个随机字符串 几乎就像一个由一定长度的 ASCII 字符组成的唯一 ID 这些字符混合有大写 小写 数字字符 有没有 Qt 库可以实现这一点 如果没有 在纯 C 中生成多个随机字
  • 有没有办法在没有 QApplication::exec() 的情况下使用 Qt?

    有没有一种安全的方法可以在不调用 QApplication exec 的情况下使用 Qt 我有许多不同的对象正在对多个资源执行长期进程 至少其中一个正在与 Web 应用程序服务器进行通信 我正在制作一个 GUI 应用程序 提示用户在正确的时
  • 程序意外完成 - QT Creator

    我正在尝试使用 QT Creator 使用 QT 框架开发 GUI 控制台应用程序 我使用的是Windows XP 我安装了QT 4 8 3和mingw 两者均已安装 没有任何错误 然后我安装了QT Creator QT 版本 路径中的 Q
  • 如何在按下托盘图标菜单操作时执行功能?

    int main int argc char argv QApplication oApp argc argv QAction action1 QMenu menu QSystemTrayIcon TrayIcon QIcon favico
  • Qt:将拖放委托给子级的最佳方式

    我在 QWidget 上使用拖放 我重新实现了 DragEnterEvent dragLeaveEvent dragMoveEvent 和 dropEvent 效果很好 在我的 QWidget 中 我有其他 QWidget 子级 我希望它们
  • Qt WinRT 应用程序无法访问文件权限被拒绝

    我需要使用 Qt 和 FFMPEG 开发 WinRT 应用程序 我根据指令构建了 WinRT 的 ffmpeghere https github com Microsoft FFmpegInterop我可以将库与我的项目链接起来 现在我需要
  • QTcpSocket 有时不发送数据

    我有两个 QT 应用程序 一个应用程序可以被认为保存了大数据 它每秒向第二个应用程序发送大约 10 KB 的数据块 之前我尝试使用QUdpSocket来传输数据 但由于MTU限制在2 5K左右 并且需要自己分割和重新组合数据 所以我改用QT
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • 如何在 Qt 中以编程方式制作一条水平线

    我想弄清楚如何在 Qt 中制作一条水平线 这很容易在设计器中创建 但我想以编程方式创建一个 我已经做了一些谷歌搜索并查看了 ui 文件中的 xml 但无法弄清楚任何内容 ui 文件中的 xml 如下所示
  • 如何在 QTabWidget Qt 中展开选项卡

    我有一个QTabWidget像这个 但我想展开选项卡以 填充 整个小部件宽度 如下所示 我怎样才能做到这一点 我在用Qt 5 3 2 and Qt 创建者 3 2 1 Update 我尝试使用setExpanding功能 ui gt myT
  • Qt:测量事件处理时间

    我想测量我的应用程序中的哪些事件在主线程中需要很长时间才能执行 阻塞 GUI 或者至少是否有任何事件花费的时间超过 比如说 10 毫秒 显然 我对需要很长时间的任务使用线程和并发 但有时很难在其他线程中放入的内容和可以保留在 GUI 中的内

随机推荐

  • Cesium修改地图颜色代码

    import as Cesium from cesium export default function modifyMap viewer Cesium Viewer 获取地图影像图层 const baseLayer viewer imag
  • 让 Flutter 在鸿蒙系统上跑起来

    鸿蒙系统 HarmonyOS 是华为推出的一款分布式操作系统 那么如何在保证开发迭代效率的前提下 以相对低的成本将移动应用快速移植到鸿蒙平台上呢 美团外卖 MTFlutter 团队近期做了一次技术探索 成功地实现了 Flutter 对于鸿蒙
  • 组合优于继承

    目录 前言 1 什么是继承 2 继承的劣势 问题 3 组合相比继承有哪些优势 4 如何判断该用组合还是继承 参考资料 前言 我们在平时日常开发设计的过程中 经常会有人提到一条经典的设计模式 组合由于继承 其实我们做更深层次的思考 我们想搞清
  • C# try catch finally return

    插眼 参考 https www cnblogs com huangshuqiang p 7850468 html
  • 三十六计之败战计

    败战计故名思意 是在败势中使用的计谋 共六计 美人计 空城计 反奸计 苦肉计 连环计和走为上 第三十一计 美人计 兵强者 攻其将 兵智者 伐其情 将弱 兵颓 其势自萎 利用御寇 顺相保也 兵强者 攻其将 兵智者 伐其情 句意 对兵力强大的敌
  • Java中的IO流详解(进阶五)

    目录 友情提醒 第一章 File类和IO技术概述 1 1 File类和IO技术的作用 1 2 创建File类对象 1 3 File类中的方法 1 4 文件过滤器 FileFileter 第二章 IO流 2 1 IO流的分类 2 2 字节输入
  • RestTemplate转发请求

    需求 服务端A接收到来自于前端的请求后 要使用RestTemplate将请求转发给服务端B 然后将服务端B的返回转发给前端 根据请求类型的不同 分为 常规请求转发 文件上传转发 文件下载转发 常规请求转发 RequestMapping tr
  • HTTP请求报错:405 Request method ‘GET‘ not supported解决方法!!(终极整理)

    1 问题场景 在项目中发送ajax请求时 控制台提示如下错误信息 org springframework web HttpRequestMethodNotSupportedException Request method GET not s
  • 【Python开发】Flask开发实战:个人博客(二)

    Flask 开发实战 个人博客 二 在 Python开发 Flask开发实战 个人博客 一 中 我们已经完成了 数据库设计 数据准备 模板架构 表单设计 视图函数设计 电子邮件支持 等总体设计的内容 本篇博客将介绍博客前台的实现 博客前台需
  • 【AnyQ】遇到的问题整理(二)

    一 version GLIBCXX 3 4 22 not found 问题描述 run server usr lib x86 64 linux gnu libstdc so 6 version GLIBCXX 3 4 22 not foun
  • 华为OD机试 - 最远足迹(Java)

    题目描述 某探险队负责对地下洞穴进行探险 探险队成员在进行探险任务时 随身携带的记录器会不定期地记录自身的坐标 但在记录的间隙中也会记录其他数据 探索工作结束后 探险队需要获取到某成员在探险过程中相对于探险队总部的最远的足迹位置 仪器记录坐
  • mongon库加上权限认证后,java程序连接异常

    现象 linux库中mongo库 用超级管理员添加了一个超级管理员 并加了认证 通过spring等相关的配置文件获取Mongo数据库连接 抛出异常 如下 Caused by org springframework data mongodb
  • MySQL——关联查询&组合查询

    1 子查询 SELECT FROM data order WHERE id IN SELECT order id FROM data order detail WHERE goods id IN SELECT id FROM data go
  • 实对称矩阵的特征值一定为实数证明

    虽然不是什么有应用价值的定理 但是每次看到实对称矩阵时总会有疑惑 现在记录下来 证明 设有实对称矩阵 A 它的特征值与对应的特征向量分别为 lambda x 另外记 overline A overline lambda overline x
  • MySQL中的多表查询详解

    目录 多表查询概述 一 多表关系 1 一对多 多对一 2 多对多 3 一对一 二 多表查询概述 1 内连接 1 1 隐式连接 1 2 显示连接 2 外连接 1 左外连接 2 右外连接 3 自连接 4 联合查询 5 子查询 5 1 按照查询结
  • 处理大并发之三 对libevent的初步认识 (必看)

    https blog csdn net feitianxuxue article details 9360347 处理大并发之三 对libevent的初步认识 首先翻译下http www wangafu net nickm libevent
  • Fisco技术文档总结1---搭建第一个区块链网络

    前言 本文的记录与总结依照于FISCO BCOS 技术文档学习联盟链搭建的相关知识 详细搭建过程见文档 本文仅作参考 本文通过在单机上部署一条4节点的FISCO BCOS联盟链 掌握FISCO BCOS部署流程 搭建 需要使用已经封装好的脚
  • 前端编写一个express实现项目部署上线流程

    1 先把写好的 vue文件打包成浏览器认识的html css js文件 使用npm run bulid命令 2 写一个express服务器 3 创建一个static文件夹 用来存放打包好的html css js文件
  • 关于Map数据类型的强转

    最近在开发项目的时候 发现有个哥们在做Map数据强转List时总是报错 现在我把有关的转换列一下方便后续查阅 1 获取Map里面的集合元素以及强转为List 判断是否为空 public void add Map bean MetaDBTem
  • 【QT】Qt多线程中的信号与槽

    QT Qt多线程中的信号与槽 1 Qt对象的依附性和事务循环 QThread继承自QObject 自然拥有发射信号 定义槽函数的能力 QThread默认声明了以下几个关键信号 信号只能声明不能定义 1 线程开始运行时发射的信号 void s