QT多线程下的信号与槽机制

2023-11-13

目录

1、QThread类

2、创建并启动线程

 3、多线程信号与槽

 4、信号与槽的调用线程?

5、调整信号与槽所在线程的依附关系

6、信号与槽的连接方式


        QT 中 QObject 作QT中类的最终父类,具有自定义信号与槽的能力,只要继承自这个类的类,也一样拥有自定义信号和槽的能力。QT 中定义信号与槽是十分有用的,QT 下多线程类QThread 是继承自 QObject,同样具有有自定义信号和槽的能力。

1、QThread类

        QThread类提供不依赖于平台的管理线程的方法。一个QThread类的对象管理一个线程,一 般从QThread继承一个自定义类,并重定义虚函数run(),在run()函数里实现线程需要完成的任务。

        将应用程序的线程称为主线程,额外创建的线程称为工作线程。一般在主线程里创建工作线程,并调用start()开始执行工作线程的任务。start()会在内部调用run()函数,进入工作线程的事件循环,在run()函数里调用exit()或quit()可以结束线程的事件循环,或在主线程里调用terminate() 强制结束线程。

        QThread类的主要接口函数、信号和槽函数见下表:

         QThread是QObject的子类,所以可以使用信号与槽机制。QThread自身定义了started()和finished()两个信号,started()信号在线程开始执行之前发射,也就是在run()函数被调用之前,finished()信号在线程就要结束时发射。

2、创建并启动线程

执行步骤:

        1)自定义类继承自QThread;

        2)实现run()函数;

        3)主函数中定义类对象并调用start()。

#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class ThreadTest: public QThread
{
public:
    ThreadTest() {}

    void run()
    {
        qDebug()<<objectName()<<" : run";
        sleep(2);
        qDebug()<<objectName()<<" : exit";
    }
};

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

    ThreadTest threada;
    threada.setObjectName(ThreadTest);
    threada.start();
    qDebug()<<"threada end";

    return a.exec();
}

结果:

 3、多线程信号与槽

        自定义信号与槽。MyClass类关联ThreadTest类信号函数,ThreadTest类实现线程创建,run()函数中发出信号,类内接收。

// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>
#include <QDebug>

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass(){}

public slots:
    void getstarted()
    {
        qDebug()<<objectName()<<" : getstarted();"
    }

    void getfinished()
    {
        qDebug()<<objectName()<" : getfinished()";
    }
};

#endif // MYCLASS_H

// threadtest.h
#ifndef THREADTEST_H
#define THREADTEST_H

#include <QThread>
#include <QDebug>

class ThreadTest: public QThread
{
    Q_OBJECT
public:
    ThreadTest()
    {
        connect(this, SIGNAL(counter(int)), this, SLOT(getCounter(int)));
        connect(this, SIGNAL(counter_reset()), this, SLOT(on_counter_reset()));
    }

    void run()
    {
        int m_counter = 0;
        for(int i=0; i<10; i++)
        {
            if(0==i%5)
            {
                m_counter = 0;
                emit counter_reset();
            }

            emit counter(m_counter);
            m_counter = m_counter + 1;
            msleep(200);
        }
    }

signals:
    void counter(int n);
    void counter_reset();

public slots:
    void getCounter(int n)
    {
        qDebug()<< "getCounter : "<<n;
    }

    void on_counter_reset()
    {
        qDebug()<<" counter reset now !";
    }
};

#endif // THREADTEST_H

main.cpp函数调用:

#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QThread>
#include myclass.h
#include threadtest.h

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ThreadTest threada;
    MyClass my;
    my.setObjectName(my);

    QObject::connect(&threada, SIGNAL(started()), &my, SLOT(getstarted()));
    QObject::connect(&threada, SIGNAL(finished()), &my, SLOT(getfinished()));
    threada.start();
    return a.exec();
}

运行结果:

 4、信号与槽的调用线程?

        一个应用程序进程中会有一个主线程,一个进程中可以有多个线程,线程拥有独立的栈空间,而栈空间专门用于函数调用(比如保存函数参数,局部变量等)。

        如果一个函数的函数体中没有访问临界资源的代码,那么这个函数可以被多个线程同时调用,不会产生任何副作用。因为线程调用函数的时候用的是自己的栈空间,既然线程的栈空间是独立的,那么谁调用函数就用谁的栈空间,互不干扰。

        所以问题来了,槽函数本质也是个函数,那么槽函数是谁调用的啊?是主线程还是定义槽函数的线程还是其他线程?

        一般来说,我们开启一个线程,那么这个线程应该会有自己的线程id。为了知道当前线程的id,QThread提供currentThreadId()方法返回当前的线程ID,函数声明如下:

static Qt::HANDLE currentThreadId() Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION;

添加线程ID打印,测试:

// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

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

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass(){}

public slots:
    void getstarted()
    {
        qDebug()<<objectName()<<":"<<"getstarted() , tid :"<<QThread::currentThreadId();
    }

    void getfinished()
    {
        qDebug()<<objectName()<<":"<<"getfinished(), tid :"<<QThread::currentThreadId();
    }
};
#endif // MYCLASS_H

// threadtest.h
#ifndef THREADTEST_H
#define THREADTEST_H

#include <QThread>
#include <QDebug>

class ThreadTest: public QThread
{
    Q_OBJECT
public:
    ThreadTest()
    {
        connect(this, SIGNAL(counter(int)), this, SLOT(getCounter(int)));
        connect(this, SIGNAL(counter_reset()), this, SLOT(on_counter_reset()));
    }

    void run()
    {
        qDebug()<<"ThreadTest tid "<<currentThreadId();
        int m_counter = 0;
        for(int i=0; i<10; i++)
        {
            if(0==i%5)
            {
                m_counter = 0;
                emit counter_reset();
            }

            emit counter(m_counter);
            m_counter = m_counter + 1;
            msleep(200);
        }
    }

signals:
    void counter(int n);
    void counter_reset();

public slots:
    void getCounter(int n)
    {
        qDebug()<< " tid:"<<currentThreadId()<< ", getCounter : " <<n;
    }

    void on_counter_reset()
    {
        qDebug()<<" tid:"<<currentThreadId()<< "counter reset now !";
    }
};

#endif // THREADTEST_H

main.cpp函数调用:

#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QThread>
#include myclass.h
#include threadtest.h

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"main tid:"<< QThread::currentThreadId();

    ThreadTest threada;
    MyClass my;

    my.setObjectName(my);

    QObject::connect(&threada,SIGNAL(started()),&my,SLOT(getstarted()));
    QObject::connect(&threada,SIGNAL(finished()),&my,SLOT(getfinished()));

    threada.start(); //开启线程执行函数
    qDebug()<<"main end";

    return a.exec();
}

运行结果:

         从运行结果看,我们的槽函数好像并不是在我们开启的线程中调用的呢,而是在主线程中。槽函数在线程类中写的,却跑到主线程中调用了,这也太奇怪了???

        问题:槽函数执行时的所在线程和信号发送操作的所在线程并不是同一个,前者位于main线程中,后者位于子线程中。下面解决。

5、调整信号与槽所在线程的依附关系

        上面测试表明,即使槽函数是定义在线程类中,调用函数的却不一定是这个线程。当然这不是我们希望的,有什么办法让调用槽函数的线程是本线程吗?

在QT中我们应该要知道几个问题:

        1)对象依附于哪个线程;

        2)对象的依附性与槽函数执行的关系;

        3)对象的依附性是否可以改变,如何改变。

        默认情况下,对象依附于自身被创建的线程。从代码中发现,是主线程创建了ThreadTest threada; 和 MyClass my;这两个对象,那么这两个对象就依附于主线程了。

        默认情况下,槽函数在对象所依附的线程中执行。所以就出现了上面情况。

对象的依附性确实可以改变,从源码中我们可以找到这样一个函数:

//在 QObject 中 
void moveToThread(QThread *thread); // 该方法定义在了 QObject 中,通过这个方法可以改变对象的依附性。

测试代码(最终版):

在main函数中添加:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<main tid:<< QThread::currentThreadId();
    ThreadTest threada;
    MyClass my;
    my.setObjectName(my);

    QObject::connect(&threada,SIGNAL(started()),&my,SLOT(getstarted()));
    QObject::connect(&threada,SIGNAL(finished()),&my,SLOT(getfinished()));

    // 改变对象依附
    threada.moveToThread(&threada);
    my.moveToThread(&threada);

    threada.start();
    qDebug()<<main end;
    return a.exec();
}

结果:

         我们发送信号后,信号进入了事件队列里啦。但是只是进入到事件队列却不去处理的话并没什么用,这就好比顾客点了菜,却没有人告诉厨师要做什么菜。调用exec();开启事件循环后就可以处理消息队列的消息了。

        信号与槽需要事件循环来支持,因为Qt4.4之后run()默认调用 QThread::exec() ,开启了事件循环,所以我们不主动在子线程中调用 exec()也能正常使用信号与槽。在4.4之前(包括4.4)我们需要自己手动开启事件循环,也就是调用QThread::exec() ,这样才能正常使用信号与槽。

为什么要改变对象的依附性?改变依附性有什么意义吗?

        前面说到多线程的时候强调过,只要用到了多线程,就可能产生临界资源竞争的情况。如果信号的发送和对应槽函数的执行在不同线程时,可能产生临界资源的竞争。

6、信号与槽的连接方式

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

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个参数的默认值,也是实际开发中字常用的连接方式。

5) Qt::UniqueConnection(单一连接)

  功能和AutoConnection相同,同样能自动确定连接类型,但是加了限制:同一个信号和同一个槽函数之间只能有一个连接。

补充阅读:

Qt多线程中的信号与槽_echo_bright_的博客-CSDN博客_qt多线程信号与槽

QT中的多线程编程:

QT中的多线程编程_洋葱汪的博客-CSDN博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT多线程下的信号与槽机制 的相关文章

  • 在 Web 浏览器中禁用 F5 [重复]

    这个问题在这里已经有答案了 可能的重复 禁用浏览器的后退按钮 https stackoverflow com questions 961188 disable browsers back button 如何禁用浏览器上的 F5 刷新 htt
  • 从 Invoke 方法获取 RETURN

    我正在尝试从另一个线程上的列表框项目中读取值 我尝试创建一种新方法来运行调用命令 我可以设法将命令发送到列表框 例如通过调用方法添加 但我似乎无法得到响应 我似乎无法获取该项目的值 我尝试了几种方法 一旦我将它从空变为字符串 事情就开始变得
  • C# 和月历,选择多个日期

    我正在制作一个程序 可以帮助人们用 C 为某个部门 预订 订单 他们需要能够选择不同月份的多个日期 我更愿意拥有它 这样他们就可以单击一个日期 然后按住 Shift 键单击另一个日期以选择这两个日期之间的所有日期 并控制单击以进行单选 取消
  • 在 Mac OS X 上安装 libxml2 时出现问题

    我正在尝试在我的 Mac 操作系统 10 6 4 上安装 libxml2 我实际上正在尝试在 Python 中运行 Scrapy 脚本 这需要我安装 Twisted Zope 现在还需要安装 libxml2 我已经下载了最新版本 2 7 7
  • 如何查明 .exe 是否正在 C++ 中运行?

    给定进程名称 例如 程序 exe C 标准库没有这样的支持 您需要一个操作系统 API 来执行此操作 如果这是 Windows 那么您将使用 CreateToolhelp32Snapshot 然后使用 Process32First 和 Pr
  • make_shared<>() 中的 WKWYL 优化是否会给某些多线程应用程序带来惩罚?

    前几天我偶然看到这个非常有趣的演示 http channel9 msdn com Events GoingNative GoingNative 2012 STL11 Magic Secrets作者 Stephan T Lavavej 其中提
  • 判断串口是普通COM还是SPP

    我正在寻找一种方法来确定 COM 是标准 COM 还是 SPP COM 也称为 COM 设备的电缆替换蓝牙适配器 我有一个可以在 USB COM gt USB 和蓝牙下工作的设备 并且蓝牙接口可以与 SPP 一起工作 我目前正在使用Syst
  • C 类型命名约定,_t 或 ALLCAPS

    我一直想知道是否有任何命名约定 例如何时对类型使用全部大写以及何时追加 t 什么时候不使用任何东西 我知道当时 K R 发布了各种有关如何使用 C 的文档 但我找不到任何相关内容 在 C 标准库类型中 t看起来漂亮占主导地位 time t
  • 无法获取本地或参数的值,因为它在此指令指针处不可用,可能是因为它已被优化掉

    Visual Studio 2010 会删除 没有其他词 不安全块中函数参数之一中的数据 什么可能导致此错误 调试器显示以下消息 Cannot obtain value of local or argument as it is not a
  • “没有合适的默认构造函数可用”——为什么会调用默认构造函数?

    我已经查看了与此相关的其他一些问题 但我不明白为什么在我的情况下甚至应该调用默认构造函数 我可以只提供一个默认构造函数 但我想了解它为什么这样做以及它会产生什么影响 error C2512 CubeGeometry no appropria
  • 如何设置消息队列的所有者?

    System Messaging MessageQueue 类不提供设置队列所有权的方法 如何以编程方式设置 MSMQ 消息队列的所有者 简短的答案是 p invoke 对 windows api 函数的调用MQSetQueueSecuri
  • MSChart 控件中的自定义 X/Y 网格线

    我有一个带有简单 2D 折线图的 C Windows 窗体 我想向其中添加自定义 X 或 Y 轴标记 并绘制自定义网格线 例如 以突出显示的颜色 虚线 我查看了 customLabels 属性 但这似乎覆盖了我仍然想显示的默认网格 这是为了
  • C 与 C++ 中的 JNI 调用不同?

    所以我有以下使用 Java 本机接口的 C 代码 但是我想将其转换为 C 但不知道如何转换 include
  • 测验;这个编译了吗?如果是的话它会返回什么(我知道答案)

    我最近发现这个错字 if name find string npos 显然开发者的意思是输入 if name find string npos 但令我惊讶的是发现错误甚至编译 Wall Werror 没有尝试过 pedantic 那么 咖啡
  • 如何测试某些代码在 C++ 中无法编译? [复制]

    这个问题在这里已经有答案了 可能的重复 单元测试编译时错误 https stackoverflow com questions 605915 unit test compile time error 我想知道是否可以编写一种单元测试来验证给
  • 如何调用与现有方法同名的扩展方法? [复制]

    这个问题在这里已经有答案了 我有这样的代码 public class TestA public string ColA get set public string ColB get set public string ColC get se
  • 值和类型的简洁双向静态 1:1 映射

    我将从我想象如何使用我想要创建的代码开始 它不必完全像这样 但它是我在标题中所说的 简洁 的一个很好的例子 就我而言 它是将类型映射到相关的枚举值 struct bar foo
  • Unity,c++ 本机插件字节数组不匹配

    在我的 C 本机插件中 我有一个调用 vector
  • Emacs C++,打开相应的头文件

    我是 emacs 新手 我想知道 是否有在头文件 源文件和相应的源文件 头文件之间切换的快捷方式 是否有像通用 emacs 参考卡那样的参考卡 Thanks There s ff find other file 您可以使用以下方法将其绑定到
  • 是否可以使用 Dapper 流式传输大型 SQL Server 数据库结果集?

    我需要从数据库返回大约 500K 行 请不要问为什么 然后 我需要将这些结果保存为 XML 更紧急 并将该文件通过 ftp 传输到某个神奇的地方 我还需要转换结果集中的每一行 现在 这就是我正在做的事情 TOP 100结果 使用 Dappe

随机推荐

  • unpkg 与 npm 的基本介绍

    目录 定义 特点 原理 使用 npm安装流程 npm install npm update registry 区别 总结 定义 UNPKG是一个基于npm registry 的静态资源 CDN 服务 它可以快速获取和使用任何JavaScri
  • springmvc中操作json,配置FastJson

    目前网络上传递数据大部分都是json和xml 但是xml使用的很少了 这里主要介绍json 一般来说json常用的场景是提供外部接口 请求内使用request域就足够了 springmvc对于json的支持 上图 1 请求json方式分析
  • 搭建AI智能语音外呼系统

    随着人工智能技术的发展 近半年来涌现了大量基于人工智能的呼叫中心业务服务商和集成商 仅电销机器人这一个方向就至少有近百家公司正在推广运营 包括百度 讯飞 智齿 硅基 百应 箭鱼 容联等 商务上的需求非常强烈 整个市场都飞快地热闹起来 一套可
  • Redis优化秒杀系统

    Redis优化秒杀系统 使用背景 普通的基于mss框架的系统在并发量不是很高的情况下 对redis的需求不是很高 redis在系统中的角色相当于一个对象缓存器 在高并发的系统中 比如秒杀系统 在某一刻对数据库中的一条数据可能是成千上万的用户
  • 前端页面生成PDF方案之puppetter

    1 新建一个文件夹 例如 test 2 新建一个js文件 例如test js 3 进入test文件夹 在该目录下运行命令行 并执行下面命令 npm init 4 运行命令安装 npm i puppetter 5 编辑test js cons
  • 常量池、运行时常量池、字符串值基本概念区分

    常量池 Constant Pool 常量池数据编译期被确定 是Class文件中的一部分 存储了类 方法 接口等中的常量 当然也包括字符串常量 字符串池 字符串常量池 String Pool String Constant Pool 是常量池
  • 微信公众平台开发笔记4(关注回复自定义消息)

    基于WXJava开发 最终目的 实现关注回复自定义消息 一条文本 一条图片 点击菜单发送视频或音频 音乐可以发送但是不能正常播放 猜测是填写的地址有问题 音频上传时需要先在接口测试平台多媒体文件上传接口上传缩略图 jpeg 新建WxMess
  • 从0实现基于Linux socket聊天室-实现聊天室的登录、注册功能-3

    上一篇我们已经讲了如何搭建一个多线程的服务器模型 可以支持多个客户端同时连接服务器 本篇我们来实现多个客户端 如何实现向服务器注册信息 并实现登录的功能 想了解更多Linux 编程知识 请关注 公众号 一口Linux 数据结构 接着上一篇的
  • Redis学习:哨兵模式

    主从切换技术的方法是 当主服务器宕机后 需要手动把一台从服务器切换为主服务器 这就需要人工干预 费事费力 还会造成一段时间内服务不可用 这不是一种推荐的方式 更多时候 我们优先考虑哨兵模式 一个典型哨兵模式 哨兵配置文件 sentinel
  • freeCAD part工作台,两个物体做合集相加的布尔组合先后顺序区别

    两个紧挨 空间尺寸上无缝隙 的物体 在part 工作台下做union的布尔操作 先选中和后选中的物体顺序还是有区别的 之前一直没注意 一直遇到问题才发现 例如有如图的2个物体 这里面的cut和fusion003已经做好了 现在我想把他组合到
  • c语言中英文转换器在线转换器,汉英转换器

    路由器之家网今天精心准备的是 汉英转换器 下面是详解 汉译英 英语是国际语言 因此我们非常重视英语的 English is an international language Thus we attach great importance
  • 深入学习jquery源码之isFunction()和isPlainObject()

    深入学习jquery源码之isFunction 和isPlainObject isArray obj 概述 测试对象是否为数组 参数 obj Object 用于测试是否为数组的对象 b append isArray b true b isF
  • 网关Netfilx Zuul:---(Eureka高可用操作)

    之前我们创建完成了3个Eureka的客户端的服务操作 你会发现我们还是没有能够通过微服来进行对他访问 还是必须通过自己服务的端口号来进行访问 那么我们的微服务是没有能够完成的 这个时候我们就需要通过网关进行操作 其实网关就是为客户端提供统一
  • 使用IntelliJ IDEA中的Spring Initializr来快速构建Spring Boot/Cloud工程

    我相信许多初学者都看了Spring Boot和Spring Cloud相关的博文中 都会涉及Spring Boot工程的创建的问题 而一般所看到的都是使用IntelliJ IDEA 工具来创建 并且方便许多 而创建的方式多种多样之前我是通过
  • 标签下载文件重命名失败,download 无效

    最近用到 a 标签实现文件下载并对文件进行重新命名 遇到了一些问题 文件重命名一直失败 所幸最终还是解决了 在此记录一下 避免后来者踩坑 HTML a 元素可以创建一个到其他网页 文件 同一页面内的位置 电子邮件地址或任何其他URL的超链接
  • 极验滑块识别-通用滑块识别

    遇到滑块问题 在写爬虫的时候 经常会遇到滑块问题 很多次都想过尝试如何攻破滑块 但是每次都没成功 除了最开始的极验滑块 当时通过原图和滑块图的对比 能够得出缺口坐标 但是随着极验 网易 腾讯滑块的更新 已经不能够找到原图了 下面给出滑块通杀
  • MATLAB中表示点形状、颜色的常见符号

    颜色字符串有 c m y r g b w 和k 分别表示青 红紫 黄 红 绿 白和黑 线型字符串有 为实线 为虚线 为点线 为点虚线 及 none 表示不用线型 标记形式有 o 和 x 填入 s 代表正方形 d 代表菱形 A 为上三角形 v
  • 2020第十一届蓝桥杯10月份省赛真题(JavaB组题解)

    2020第十一届蓝桥杯10月份省赛真题 JavaB组题解 试题 A 门牌制作 试题 B 寻找 2020 试题 C 蛇形填数 试题 D 七段码 试题 E 排序 试题 F 成绩分析 试题 G 单词分析 试题 H 数字三角形 试题 I 子串分值和
  • 代码质量的评价标准

    如何评价代码质量 代码质量的评价比较主观 一般会使用以下几个词汇 可读性 可扩展性 可维护性 灵活 优雅 可重用性 可测试性 这些是从不同方面来评价 但是各个维度都彼此关联 譬如可读性和可扩展性好 我们就说这段代码的可维护性比较好 代码质量
  • QT多线程下的信号与槽机制

    目录 1 QThread类 2 创建并启动线程 3 多线程信号与槽 4 信号与槽的调用线程 5 调整信号与槽所在线程的依附关系 6 信号与槽的连接方式 QT 中 QObject 作QT中类的最终父类 具有自定义信号与槽的能力 只要继承自这个