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

article/2025/10/1 13:11:01

目录

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.hint 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.hint 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博客


http://chatgpt.dhexx.cn/article/0OBBsky5.shtml

相关文章

QT—信号与槽详解

目录 一、什么是信号与槽 二、信号与槽的添加 1.添加槽方法 2.添加信号 3.发送信号 4.信号与槽连接 三、连接类型 1.一对一 2.一对多 3.多对一 四、信号与槽断开连接 1.断开一个对象的所有信号关联 2.断开指定信号的所有关联 3.断开指定接收者的所有关联 4.断…

Qt的信号与槽

引入 在GUI编程中&#xff0c;组件组件如何实现通信是核心的技术内容。 Qt使用了信号与槽的机制&#xff0c;为此Qt引入了一些关键字slots、signals、emit&#xff0c;这些都是Qt特有的关键字&#xff0c;然后这些关键字会被Qt的moc转换位标准的C语句。 Qt 的部件类中有一些已…

Linux下的QT信号和槽机制(二)

目录 1.基本知识点 2.信号机制 3.槽机制 4.元对象工具 5.信号和槽机制原理 &#xff08;1&#xff09;信号和槽机制 &#xff08;2&#xff09;信号和槽函数的关联 &#xff08;3&#xff09;信号和槽的断开 &#xff08;4&#xff09;信号和槽的优点 &#xff08;5&…

Qt信号与槽原理

Qt信号与槽原理 本文为原创文章&#xff0c;转载请注明出处&#xff0c;或注明转载自“黄邦勇帅(原名&#xff1a;黄勇) 本文出自本人原创著作《Qt5.10 GUI完全参考手册》网盘地址&#xff1a; https://pan.baidu.com/s/1iqagt4SEC8PUYx6t3ku39Q 《C语法详解》网盘地址&#…

Qt 之 信号槽机制及优缺点

1. Qt 信号槽机制 概念&#xff1a; 信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽&#xff0c;实际就是观察者模式。当某个事件发生之后&#xff0c;比如&#xff0c;按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号&#xff08;signal&#xff09;。这种发出是…

【Qt】一篇全面的信号和槽函数机制总结

信号和槽函数机制 文章目录 信号和槽函数机制一、信号和槽机制简介二、【信号】&#xff08;2-1&#xff09;信号的发出&#xff08;2-2&#xff09;信号的处理 三、【槽函数】&#xff08;3-1&#xff09;带有默认参数的信号和槽函数&#xff08;3-2&#xff09;使用QObject::…

Qt: 信号与槽机制

一、信号和槽机制是什么&#xff1f; 1 Qt信号槽机制&#xff1a;是Qt的核心机制&#xff0c;它是Qt定义的一种对象间的通讯机制&#xff0c;且独立于标准C/C语言。 2 信号&#xff08;signals&#xff09;:当某个类对象发生内部改变时&#xff0c;发射“信号”随后与关联的“槽…

Qt 信号和槽的机制(逻辑清晰的来说清楚信号和槽,呕心沥血之作)

Qt 信号和槽的机制 首先说声对不起&#xff0c;上次在PyQt5中写信号与槽&#xff0c;由于时间原因没有写完。有小伙伴留言说&#xff0c;希望把这章补全。所以&#xff0c;这是一篇迟来的文章&#xff0c;再次向大家说声抱歉。 一、桌面程序的结构 Qt的使用场景&#xff0c;…

Qt信号和槽机制emit的使用

1.相关概念&#xff1a; [1].信号&#xff08;Signal&#xff09;就是在特定情况下被发射的事件 [2].槽&#xff08;Slot&#xff09;就是对信号响应的函数。槽就是一个函数 [3].信号与槽之间的关联&#xff1a;是用 QObject::connect() 函数实现的&#xff0c;其基本格式是…

Qt信号和槽机制详解

Qt信号和槽机制详解 信号和槽是 Qt 特有的消息传输机制&#xff0c;它能将相互独立的控件关联起来。 举个简单的例子&#xff0c;按钮和窗口本是两个独立的控件&#xff0c;点击按钮并不会对窗口造成任何影响。通过信号和槽机制&#xff0c;我们可以将按钮和窗口关联起来&…

Qt信号与槽机制

一. 简介 就我个人来理解&#xff0c;信号槽机制与Windows下消息机制类似&#xff0c;消息机制是基于回调函数&#xff0c;Qt中用信号与槽来代替函数指针&#xff0c;使程序更安全简洁。 信号和槽机制是 Qt 的核心机制&#xff0c;可以让编程人员将互不相关的对象绑定在一起&…

Qt --- 信号与槽

信号与槽概述 信号与槽是 Qt 框架引以为豪的机制之一。所谓信号与槽&#xff0c;实际就是观察者模式(发布-订阅模式)。当某个事件发生之后&#xff0c;比如&#xff0c;按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号&#xff08;signal&#xff09;。这种发出是没…

Qt中的信号和槽详解

一、背景介绍 信号和槽用于两个对象之间的通信。信号和槽机制是Qt的核心特征&#xff0c;也是Qt不同于其他开发框架的最突出特征。在GUI编程中&#xff0c;当改变了一个部件时&#xff0c;总希望其他部件也能了解到该变化。更一般来说&#xff0c;我们希望任何对象都可以和其他…

QT笔记(一)

学习目标&#xff1a; 总结学习的QT 学习内容&#xff1a; 1、 QT的一些固定格式 2、 控件和事件 3、 信号和槽 记录内容&#xff1a; 1、 QT的固定格式 (1) 引用头文件 自己创建的头文件用" "括起&#xff0c;eg: #include "mainwindow.h"QT提供的头文…

QT中信号和槽详解

一、QT中信号和槽的机制 1、贴图来理解信号和槽的关系 2、解释 &#xff08;1&#xff09;信号和槽是用于QT对象之间的通信&#xff0c;信号可以有某种动作触发&#xff0c;也可以直接由代码触发。 &#xff08;2&#xff09;槽也叫操函数&#xff0c;当完成了信号和槽的连接…

Qt核心特性之 —— 「信号(Signal)与槽(Slot)」机制

目录 1、Qt 与 Qt Creator简介&#xff1a; 2、关于引用头文件的一些事儿&#xff1a; 3、信号(Signal)与槽(Slot)机制&#xff1a; 3.1、一个小例子&#xff1a; 4、自定义信号与槽&#xff1a; 4.1、运行效果&#xff1a; 5、信号与槽的特性&#xff1a; 6、Qt 4…

三张图搞定TCP 握手、HTTPS、TLS加密过程

1. 抓包内容&#xff08;WireShark&#xff09; 2. 搞定握手、挥手、SSL加密过程 3. 消息内容&#xff08;Charles&#xff09; 之前看到写的比较好的文章&#xff0c;有文字详细叙述&#xff1a; TLS版本差异 https://zhuanlan.zhihu.com/p/27524995?utm_source协议解析 htt…

TCP三次握手学习心得

客户端A若要连接服务端B&#xff0c;首先A会向B发送一个连接请求&#xff0c;其中SYN1&#xff0c;ACK0&#xff0c;B为了告诉A成功收到了消息&#xff0c;则向A发送一个确认包&#xff0c;其中SYN1,ACK1,这时A收到之后又会向B发送一个确认收到确认包的确认包&#xff0c;SYN0,…

使用wireshark抓取Tcp三次握手

文章目录 wireshark的下载安装TCP协议段格式简单介绍确认应答机制介绍使用wireshark抓取TCP的三次握手 wireshark的下载安装 软件的下载可以直接去官网下载 wireshark&#xff0c;选择自己电脑适合的版本就行。 但是不咋推荐&#xff0c;原因是国外网站访问速度太慢&#xff…

TCP的三次握手、四次挥手

一、TCP的三次握手 第一次握手&#xff1a;你能和我建立连接吗&#xff0c;可以接受到我的数据吗。 SYN 1 &#xff0c;seq x 第二次握手&#xff1a;可以建立连接&#xff0c;我接受到你的请求了&#xff0c;能接受到我的数据吗&#xff0c;你的数据是这个吗 SYN 1 &#…