Qt中槽函数在那个线程执行的探索和思考

      信号和槽是Qt的核心机制之一,通过该机制大大简化了开发者的开发难度。信号和槽属于观察者模式(本质上是回调函数的应用)。是函数就需要考虑其是在那个线程中执行,本文讨论的就是槽函数在那个线程中执行的问题。

目录

1. connect函数的第五个参数说明

2. 发送者和接收者在同一个线程创建

2.1 槽函数在发送者所在线程执行

2.3 槽函数不在发送者所在线程执行

3. 发送者和接收者在不同线程创建

3.1 槽函数在接收者所在线程执行

3.2 槽函数在发送者所在线程执行

4.发送者所在线程和发送信号线程的区别


1. connect函数的第五个参数说明

       connect函数用来连接信号和槽,类似于提前注册。其第五个参数默认是Qt::AutoConnection,所以开发者很多时候可以忽略此参数。但是在牵扯到复杂业务开发,尤其是多线程并发开发时往往需要关注第五个参数,第五个参数取值和含义如下:

  • Qt::AutoConnection

    自动连接,发送者和接受者在同一个线程时等同于Qt::DirectConnection,不同线程等同于Qt::QueuedConnection

  • Qt::DirectConnection

    直接(同步)连接,槽函数在接受者所依附线程执行。

  • Qt::QueuedConnection

    队列(异步)连接,槽函数在发送信号的线程执行。异步连接的时候,信号是由接收者所在的线程的事件处理机制来处理的,如果接收者所在的线程没有事件处理的话,这个信号就不会被处理

  • Qt::BlockingQueuedConnection

    阻塞连接,发送者和接收者在同一线程时会死锁

  • Qt::UniqueConnection

    唯一连接,防止信号和槽重复连接

  • Qt::SingleShotConnection

    单次连接,信号和槽函数只连接一次,槽函数执行后连接会自动断开

2. 发送者和接收者在同一个线程创建

2.1 槽函数在发送者所在线程执行

发送者

sender.h

#ifndef SENDER_H
#define SENDER_H#include <QObject>
#include <QString>class Sender :public QObject {Q_OBJECTpublic:Sender(QObject* parent = 0);public slots :void emitsig(const QString& str, const int& ci);signals:void sig(const QString& str, const int& ci);
};#endif // SENDER_H

sender.cpp

#include "sender.h"
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>Sender::Sender(QObject* parent) : QObject(parent) {}void Sender::emitsig(const QString& str, const int& ci)
{qDebug() << "sender signal thread id is: " << QThread::currentThreadId()<< str << ci;emit sig(str, ci);
}

接收者

recver.h

#ifndef RECVER_H
#define RECVER_H#include <QObject>
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>class Recver : public QObject
{Q_OBJECT
public:explicit Recver(QObject *parent = nullptr);public slots :void slot(const QString& str, const int& ci);
};#endif // RECVER_H

recver.cpp

#include "recver.h"
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>Recver::Recver(QObject *parent): QObject{parent}
{}void Recver::slot(const QString& str, const int& ci) {qDebug() << "recver slot thread id is: " << QThread::currentThreadId()<< str << ci;
}

main函数

main.cpp

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>#include "sender.h"
#include "recver.h"
#include "mythread.h"/*
// QObject::connect函数的第五个参数:Qt::AutoConnection:自动连接,发送者和接受者在同一个线程时等同于Qt::DirectConnection,不同线程等同于Qt::QueuedConnectionQt::DirectConnection:直接调用连接,槽函数在接受者所依附线程执行。Qt::QueuedConnection:异步调用连接,槽函数在发送信号的线程执行。异步连接的时候,信号是由接收者所在的线程的事件处理机制来处理的,如果接收者所在的线程没有事件处理的话,这个信号就不会被处理Qt::BlockingQueuedConnection:阻塞连接调用,发送者和接收者在同一线程时会死锁Qt::UniqueConnection:防止信号和槽重复连接Qt::SingleShotConnection:信号和槽函数仅只需单次连接,槽函数执行后连接会自动断开
*//*
// Qt4和Qt5都适用的连接方式,注意:此方式是在Q5上可能会导致槽函数不被调用,此时可尝试使用Qt5新的连接方式
QObject::connect(&sender, SIGNAL(sig()), &recver, SLOT(slot()), Qt::DirectConnection);
QObject::connect(&sender, SIGNAL(sig()), &recver, SLOT(slot()), Qt::QueuedConnection);
//QObject::connect(&sender, SIGNAL(sig()), &recver, SLOT(slot()), Qt::BlockingQueuedConnection);
QObject::connect(&sender, SIGNAL(sig()), &recver, SLOT(slot()), Qt::UniqueConnection);
QObject::connect(&sender, SIGNAL(sig()), &recver, SLOT(slot()), Qt::SingleShotConnection);
*//*
// Qt5最新的连接方式
QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot, Qt::DirectConnection);
QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot, Qt::QueuedConnection);
// 发送者和接收者在同一个线程,将会死锁
//QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot, Qt::BlockingQueuedConnection);
//QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot, Qt::UniqueConnection);
QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot, Qt::SingleShotConnection);
*/// 主线程获取
/*
QCoreApplication::instance()->thread();
*//*
// 关于Qt线程的使用说明
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。
而从Qt4.4开始,qthreads-no-longer-abstract,run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject就够了
QThread中run对于线程的作用相当于main函数对于应用程序。它是线程的入口,run的开始和结束意味着线程的开始和结束。
QThread所依附的线程,就是创建线程的线程。
QThread管理的线程,就是run中创建的线程。
*/// 连接多次,发送信号槽函数也会多次触发
void test1() {Sender sender;Recver recver;QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot);QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot);qDebug() << "call first";sender.emitsig("fist", 110);qDebug() << "call second";sender.emitsig("second", 220);qDebug() << "call third";sender.emitsig("third", 330);
}// 接受对象虽然是线程对象,但是槽函数却在发送对象所在线程对象执行,因为接收者依附于发送对象所在线程
/*
QThread中slot和run函数共同操作的对象,都会用QMutex锁住是因为slot和run处于不同线程,需要线程间的同步。
*/
void test2() {Sender sender;Mythread mythread;// Mythread构造函数中去掉moveToThread(this);,槽函数将在主线程执行,加上moveToThread(this)槽函数将在子线程执行// 加上moveToThread(this)后连接方式修改为Qt::DirectConnection,槽函数在主线程中执行QObject::connect(&sender, SIGNAL(sig(const QString&, const int&)), &mythread, SLOT(slot_main(const QString&, const int&)));mythread.start();sender.emitsig("mythread", 440);
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "main thead id is : " << QThread::currentThreadId();//std::cout << std::this_thread::get_id() << std::endl;Sender sender;Recver recver;QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot);sender.emitsig("fist", 110);return a.exec();
}

运行效果:

可以看到槽函数在信号发送者所在线程(主线程)中执行。

2.3 槽函数不在发送者所在线程执行

main.cpp

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>#include "sender.h"
#include "recver.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "main thead id is : " << QThread::currentThreadId();//std::cout << std::this_thread::get_id() << std::endl;Sender sender;Recver recver;QObject::connect(&sender, &Sender::sig, &recver, &Recver::slot);QThread thread;recver.moveToThread(&thread);thread.start();sender.emitsig("fist", 110);return a.exec();
}

运行效果:

代码中创建一个子线程对象,并将接收者移动到子线程,槽函数在子线程中执行。

3. 发送者和接收者在不同线程创建

3.1 槽函数在接收者所在线程执行

mythread.h

#ifndef MYTHRED_H
#define MYTHRED_H#include <QThread >class Mythread : public QThread
{
Q_OBJECTpublic:Mythread(QObject* parent = 0);public slots:void slot_main(const QString& str, const int& ci);protected:void run();
};#endif // MYTHRED_H

mythread.cpp

#include "mythread.h"
#include <QDebug>
#include "sender.h"Mythread::Mythread(QObject* parent) : QThread(parent)
{//moveToThread(this);
}void Mythread::slot_main(const QString& str, const int& ci) {qDebug() << "mythread slot_main: " << currentThreadId() << str << ci;
}void Mythread::run() {qDebug() << "mythread thread: " << currentThreadId();Sender sender;connect(&sender, SIGNAL(sig(const QString&, const int&)), this, SLOT(slot_main(const QString&, const int&)));sender.emitsig("thread", 220);exec();
}

main.cpp

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>#include "sender.h"
#include "recver.h"
#include "mythread.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "main thead id is : " << QThread::currentThreadId();//std::cout << std::this_thread::get_id() << std::endl;Mythread mythread;mythread.start();return a.exec();
}

运行效果如下:

如上显示在子线程发送信号,槽函数却在主线程中执行,因为接收者mythread是在主线程中创建。

3.2 槽函数在发送者所在线程执行

mythread.cpp

#include "mythread.h"
#include <QDebug>
#include "sender.h"Mythread::Mythread(QObject* parent) : QThread(parent)
{moveToThread(this);
}void Mythread::slot_main(const QString& str, const int& ci) {qDebug() << "mythread slot_main: " << currentThreadId() << str << ci;
}void Mythread::run() {qDebug() << "mythread thread: " << currentThreadId();Sender sender;connect(&sender, SIGNAL(sig(const QString&, const int&)), this, SLOT(slot_main(const QString&, const int&)));sender.emitsig("thread", 220);exec();
}

main.cpp

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>#include "sender.h"
#include "recver.h"
#include "mythread.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "main thead id is : " << QThread::currentThreadId();//std::cout << std::this_thread::get_id() << std::endl;Mythread mythread;mythread.start();return a.exec();
}

运行效果:

      将信号接收者构造函数//moveToThread(this);的注释放开,槽函数在子线程中执行。尽管接收者在主线程中被创建。

如果构造函数注释moveToThread(this);,connect第五个参数修改为Qt::DirectConnection,槽函数也可以在子线程中执行,如下:

connect(&sender, SIGNAL(sig(const QString&, const int&)), this, SLOT(slot_main(const QString&, const int&)), Qt::DirectConnection);

mythread.cpp

#include "mythread.h"
#include <QDebug>
#include "sender.h"Mythread::Mythread(QObject* parent) : QThread(parent)
{//moveToThread(this);
}void Mythread::slot_main(const QString& str, const int& ci) {qDebug() << "mythread slot_main: " << currentThreadId() << str << ci;
}void Mythread::run() {qDebug() << "mythread thread: " << currentThreadId();Sender sender;connect(&sender, SIGNAL(sig(const QString&, const int&)), this, SLOT(slot_main(const QString&, const int&)), Qt::DirectConnection);sender.emitsig("thread", 220);exec();
}

信号发送者在子线程,槽函数也在子线程中执行。

4.发送者所在线程和发送信号线程的区别

       有很多人分不清发送者所在线程和发送信号的线程,在此做一下区分说明。发送者所在线程指的是构造发送者对象所在的线程,发送信号线程指的是发送信号所在的线程,即调用emit所在的线程,有些时候这两个线程并不是一个线程。前面说的槽函数在发送者所在线程执行指的是在发送者所在线程,而不是发送信号的线程。如下代码:

mythread.h

#ifndef MYTHRED_H
#define MYTHRED_H#include <QThread>
#include "sender.h"class Mythread : public QThread
{
Q_OBJECTpublic:Mythread(QObject* parent = 0);Mythread(Sender* sender);public slots:void slot_main(const QString& str, const int& ci);protected:void run();private:Sender* m_sender;
};#endif // MYTHRED_H

mythread.cpp

#include "mythread.h"
#include <QDebug>Mythread::Mythread(QObject* parent) : QThread(parent)
{//moveToThread(this);m_sender = nullptr;
}Mythread::Mythread(Sender* sender) {//moveToThread(this);m_sender = sender;
}void Mythread::slot_main(const QString& str, const int& ci) {qDebug() << "mythread slot_main: " << currentThreadId() << str << ci;
}void Mythread::run() {qDebug() << "mythread thread: " << currentThreadId();if (m_sender) {connect(m_sender, SIGNAL(sig(const QString&, const int&)), this, SLOT(slot_main(const QString&, const int&)));m_sender->emitsig("thread", 220);}exec();
}

main.cpp

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <iostream>
#include <thread>
#include <QObject>#include "sender.h"
#include "recver.h"
#include "mythread.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "main thead id is : " << QThread::currentThreadId();//std::cout << std::this_thread::get_id() << std::endl;Sender sender;Mythread mythread(&sender);mythread.start();return a.exec();
}

运行效果如下:

       如上,发送者对象在主线程创建,在子线程中发送信号,槽函数是在主线程中执行,而并非在子线程中执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226997.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据存储技术(3)—— HBase分布式数据库

目录 一、HBase简介 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;特点 &#xff08;三&#xff09;HBase架构 二、HBase原理 &#xff08;一&#xff09;读流程 &#xff08;二&#xff09;写流程 &#xff08;三&#xff09;数据 flush 过程 &#xf…

IS-IS原理与配置3

IS-IS原理与配置 • IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP &#xff08;ConnectionL…

OSWBB 部署实现

1、OSWatcher (oswbb) 是一个可供用户下载的工具&#xff0c;可以用来抓取操作系统的性能指标。 是一组shell程序&#xff0c;程序中调用: top, vmstat, iostat, mpstat, netstat,and traceroute等os的监控工具 。OSWatcher 的使用是基于 standard licensing terms 并且不需要…

Alibaba分布式事务组件Seata XATCC实战

1. Seata XA模式实战 XA协议最主要的作用是就是定义了RM-TM的交互接口&#xff0c;XA规范除了定义的RM-TM交互的接口(XA Interface)之外&#xff0c;还对两阶段提交协议进行了优化。 1.1 整体机制 在 Seata 定义的分布式事务框架内&#xff0c;利用事务资源&#xff08;数据…

python3GUI--仿win风格天气By:PyQt5

文章目录 一&#xff0e;前言二&#xff0e;展示1.首页-白色1.首页-白色22.首页-黑色3.天气预报视频4.天气资讯-白色5.天气资讯-黑色6.收藏夹-白色7.收藏夹-黑色8.搜索9.mini-白色10.mini-黑色11.光遇天气 三&#xff0e;心得四&#xff0e;总结五&#xff0e;参考 一&#xff…

编译 pywinhook v1.6.2 的环境设置和步骤

准备做一个鼠标事件响应程序。 查了一下相关python的第三方类库&#xff0c;发现有 pyhook。 一、起源 pyhook 1、pyhook是最早的版本 pyhook支持的python版本比较低&#xff0c;代码在 https://sourceforge.net/projects/pyhook/ 2、之后产生了两个并行版本 pyHook3 和 p…

来聊聊Spring的循环依赖

文章目录 首先了解一下什么是循环依赖简述解决循环依赖全过程通过debug了解Spring解决循环依赖全过程Aservice的创建递归来到Bservice的创建然后BService递归回到了getAservice的doGetBean中故事再次回到Aservice填充BService的步骤 总结成流程图为什么二级就能解决循环依赖问题…

【Qt开发流程】之UDP

概述 UDP (User Datagram Protocol)是一种简单的传输层协议。与TCP不同&#xff0c;UDP不提供可靠的数据传输和错误检测机制。UDP主要用于那些对实时性要求较高、对数据传输可靠性要求较低的应用&#xff0c;如音频、视频、实时游戏等。 UDP使用无连接的数据报传输模式。在传…

如何实现订单自动取消

由于Redis具有过期监听的功能&#xff0c;于是就有人拿它来实现订单超时自动关闭的功能&#xff0c;但是这个方案并不完美。今天来聊聊11种实现订单超时自动关闭的方案&#xff0c;总有一种适合你&#xff01;这些方案并没有绝对的好坏之分&#xff0c;只是适用场景的不大相同。…

图的搜索(二):贝尔曼-福特算法、狄克斯特拉算法和A*算法

图的搜索&#xff08;二&#xff09;&#xff1a;贝尔曼-福特算法、狄克斯特拉算法和A*算法 贝尔曼-福特算法 贝尔曼-福特&#xff08;Bellman-Ford&#xff09;算法是一种在图中求解最短路径问题的算法。最短路径问题就是在加权图指定了起点和终点的前提下&#xff0c;寻找从…

Vue3使用了Vite和UnoCSS导致前端项目启动报错:Error:EMFILE:too many open files

一个 Vue3 的项目&#xff0c;用的是 Vite 打包&#xff0c;通过 npm run dev 运行时&#xff0c;遇到了以下错误&#xff08;尤其是引入了 Element-Plus 后&#xff09;&#xff1a; Error: EMFILE: too many open files&#xff0c;后面是具体的文件路径。。甚至到了 node_mo…

5G工业物联网网关,比4G工业网关强在哪里?

​随着5G技术的广泛应用&#xff0c;越来越多的行业开始探索如何利用5G网络提升效率和创新能力。其中&#xff0c;工业物联网领域是受益最大的领域之一。作为连接物联网设备和网络的关键组件&#xff0c;5G工业物联网网关在这个变革中发挥着至关重要的作用。本文将深入探讨5G工…

指针进阶篇

指针的基本概念&#xff1a; 指针是一个变量&#xff0c;对应内存中唯一的一个地址指针在32位平台下的大小是4字节&#xff0c;在64位平台下是8字节指针是有类型的&#xff0c;指针类型决定该指针的步长&#xff0c;即走一步是多长指针运算&#xff1a;指针-指针表示的是两个指…

赛氪为第五届全球校园人工智能算法精英大赛决赛选手保驾护航

12月10日&#xff0c;以“智青春算未来”为主题的2023年第五届全球校园人工智能算法精英大赛全国总决赛在河海大学江宁校区举行。本次大赛由江苏省人工智能学会主办&#xff0c;自9月份启动以来&#xff0c;共吸引了全国近400所高校的3000多支参赛团队参加。经过校赛、省赛选拔…

nlp与cv的发展

Transformer的出现,促进了更高容量模型的建立,为大模型的出现奠定基础. &#x1f9d0;大模型通常具有十亿个以上参数(仅供参考) &#x1f62e;左边的蓝色是CV领域、右下绿色是NLP、右上蓝色是多模态&#x1f603;基础模型(Foundational Models)首次由Bommasani等人在《Stanford…

HTTP代理服务器脚本录制

1、报错1 target controller is configured to “use recording Controller“ but no such controller exists,ensure_target controller is configured to "use recording -CSDN博客

等等Domino 14.0FP1

大家好&#xff0c;才是真的好。 节奏确实太快了&#xff0c;有时候我深感我也追不上。 以前Notes Domino是三年磨一剑&#xff0c;也就说每三年才发一个大版本&#xff0c;从2019年开始&#xff0c;进行了高频提速&#xff0c;居然一年一个大版本&#xff01; 周末&#xf…

NAT——网络地址转换

目录 一、概念 二、NAT的分类 1.静态NAT 1.1 静态NAT的配置 1.2 利用eNSP小实验加强对静态NAT的理解 2、动态NAT 三、NAPT——端口映射 四、Easy IP 使用一个公网地址可以让所有人都上公网 一、概念 随着Internet的发展和网络应用的增多&#xff0c;IPv4地址枯竭已经成为…

jmeter 如何循环使用接口返回的多值?

有同学在用jmeter做接口测试的时候&#xff0c;经常会遇到这样一种情况&#xff1a; 就是一个接口请求返回了多个值&#xff0c;然后下一个接口想循环使用前一个接口的返回值。 这种要怎么做呢&#xff1f; 有一定基础的人&#xff0c;可能第一反应就是先提取前一个接口返回…

在Node.js中MongoDB排序的方法

本文主要介绍在Node.js中MongoDB排序的方法。 目录 Node.js中MongoDB排序使用原生的mongodb驱动程序进行排序使用Mongoose库中的排序 Node.js中MongoDB排序 在Node.js中使用MongoDB进行排序&#xff0c;可以使用原生的mongodb驱动程序或者Mongoose库。 使用原生的mongodb驱动…