热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

QThread事件循环

事件循环详细解释可以点击该链接:Qt实用技能3-理解事件循环-知乎(zhihu.com)子类化QThread的事件循环:事件循环的作用:

事件循环详细解释可以点击该链接:Qt实用技能3-理解事件循环 - 知乎 (zhihu.com) 


子类化QThread的事件循环: 


事件循环的作用:


在run()函数中添加事件循环后,run()函数中的内容执行完后,线程还会运作,启动事件循环后,主线程还可以和子线程通过信号与槽的方式继续使用。

  1. 使用exec()来启动事件循环
  2. exec()后面的内容将不会运行,直到退出事件循环
  3. 使用quit()退出事件循环
  4. 只有槽函数所在线程开启了事件循环,它才能在对应信号发射后被调用
  5. 无论事件循环是否开启,信号发送后会直接进入槽函数所依附的线程的事件队列


首先要了解一下connect中的槽函数所依附的线程:

线程: My_thread.cpp文件

#include "my_thread.h"
#include
My_thread::My_thread(QObject *parent) : QThread(parent)
{
}
void My_thread::run()//重写run函数
{
qDebug()<<"run中的线程号&#xff1a;"< exec();//开启事件循环
}
void My_thread::show_Slot_place()//槽函数
{
qDebug()<<"槽函数的线程号"<}

主类&#xff1a;Widget.cpp文件

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
qDebug()<<"主线程的位置&#xff1a;"< My_thread *thread&#61;new My_thread;
connect(this,&Widget::show_Slot,thread,&My_thread::show_Slot_place);
thread->start();//开启线程
emit show_Slot();//触发信号
}

上面的数据可以看出&#xff1a;该槽函数是在主线程中执行的


如何让槽函数在run&#xff08;&#xff09;函数运行&#xff1a;



  • 在线程类中的构造函数中添加  movetoThread&#xff08;this&#xff09;


在线程类中的构造函数中添加  movetoThread&#xff08;this&#xff09;


创建对象时&#xff0c;把自己依附到次线程中。

 线程&#xff1a; My_thread.cpp文件

子类化QThread退出线程的方法&#xff1a; 



  • 无事件循环&#xff1a;run函数中有while循环的话&#xff0c;设置一个退出条件即可。

  • 有事件循环&#xff1a;先退出while循环&#xff0c;再quit&#xff08;&#xff09;退出消息队列&#xff0c;wait&#xff08;&#xff09;等待线程执行完毕



 子类化QThread释放线程内存的方法&#xff1a;



  • 有父类的话&#xff0c;在父类的析构函数中使线程执行完毕&#xff0c;然后会跟着父类释放内存
  • 无父类的话&#xff0c;使用deleteLater()函数销毁


使用子类化QObject&#xff0c;使用movetoThread(thread)


使用这种方法的话&#xff0c;默认拥有事件循环&#xff0c;且处在事件循环中。

子类object&#xff1a;

#include "object.h"
#include
object::object(QObject *parent) : QObject(parent)
{
}
void object::show_Slot()
{
qDebug()<<"槽函数的线程&#xff1a;"<}

主类&#xff1a;

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QThread *thread&#61;new QThread;
object * object1&#61;new object;
qDebug()<<"主线程号&#xff1a;"< object1->moveToThread(thread);
connect(this,&Widget::show_Slots,object1,&object::show_Slot);
thread->start();
emit this->show_Slots();
}

 这种方法&#xff0c;比子类化QThread更方便的在子线程中执行槽函数


 释放资源的方法&#xff1a;


使用deleteLater&#xff08;&#xff09;函数释放资源&#xff0c;释放资源会变成野指针&#xff0c;所以还要把指针置空。


//释放堆空间资源
connect(m_th, &QThread::finished, m_obj, &QObject::deleteLater);
connect(m_th, &QThread::finished, m_th, &QObject::deleteLater);
//设置野指针为nullptr
connect(m_th, &QObject::destroyed, this, &MainWindow::SetPtrNullptr);
connect(m_obj, &QObject::destroyed, this, &MainWindow::SetPtrNullptr);
//消除野指针
void SetPtrNullptr(QObject *sender){
if(qobject_cast(m_th) &#61;&#61; sender){
m_th &#61; nullptr;
qDebug("set m_th &#61; nullptr");
}
if(qobject_cast(m_obj) &#61;&#61; sender){
m_obj &#61; nullptr;
qDebug("set m_obj &#61; nullptr");
}
}


线程和 QObjects

QThread继承了QObject&#xff0c;QObject可以在多个线程中使用&#xff0c;发出调用其他线程中槽的信号&#xff0c;并将事件发布到在其他线程中“活动”的对象。这是可能的&#xff0c;因为允许每个线程都有自己的事件循环。

QObject是可以重入的&#xff0c;大多数非GUI子类都是可重入的。

QTimer&#xff0c;QTcpSocket&#xff0c;QUdpSocket&#xff0c;QProcess&#xff0c;在多线程中使用这些类时&#xff1a;


  • 必须始终在创建父类的线程中创建QObject的子级
  • 事件驱动对象只能在单个线程中使用
  • 再删除QThread之前&#xff0c;必须确保删除线程中创建的所有对象。

通常&#xff0c;不支持在 QApplication 之前创建 QObjects&#xff0c;这可能会导致退出时出现奇怪的崩溃&#xff0c;具体取决于平台。这意味着 QObject 的静态实例也不受支持的。结构合理的单线程或多线程应用程序应使 QApplication 成为第一个创建&#xff0c;最后一个销毁的 QObject。


线程的事件循环&#xff1a;

每个线程都可以有自己的事件循环&#xff0c;线程中的事件循环使线程可以使用某些需要存在事件循环的非 GUI Qt 类&#xff08;例如 QTimer、QTcpSocket 和 QProcess&#xff09;。它还可以将来自任何线程的信号连接到特定线程的插槽。


从其他线程访问QObject子类&#xff1a;

QObject和其所有子类都不是线程安全的&#xff0c;当您从另一个线程访问对象时&#xff0c;事件循环可能会将事件传递到QObject子类。如果要在当前线程中不存在的 QObject 子类上调用函数&#xff0c;并且该对象可能会接收事件&#xff0c;则必须使用互斥锁保护对 QObject 子类内部数据的所有访问;否则&#xff0c;您可能会遇到崩溃或其他不良行为。

注意&#xff1a;QThread对象创建于对象线程中&#xff0c;而run&#xff08;&#xff09;函数再子线程中&#xff0c;所以在QThread子类中提供插槽通常是不安全的&#xff0c;需要使用互斥锁保护成员变量。但从run&#xff08;&#xff09;函数中发射信号是线程安全的。

跨线程的信号和插槽&#xff1a;

信号的连接方式有&#xff1a;

Qt&#xff1a;&#xff1a;AutoConnection&#xff08;默认&#xff09;

接收对象有关联的线程中发出信号&#xff0c;直接连接。

否则&#xff0c;排队连接

Qt&#xff1a;&#xff1a;DirectConnec发出信号时&#xff0c;将立即调用该插槽。该插槽在信令线程中执行
Qt::QueuedConnection当控制返回到接收器线程的事件循环时&#xff0c;将调用该槽。该插槽在接收器的线程中执行。
Qt::BlockingQueuedConnection与 Qt&#xff1a;&#xff1a;QueuedConnection 相同&#xff0c;不同之处在于信令线程阻塞&#xff0c;直到插槽返回。如果接收器位于信令线程中&#xff0c;则不得使用此连接&#xff0c;否则应用程序将死锁。
Qt::UniqueConnection这是一个标志&#xff0c;可以使用按位 OR 与上述任何一种连接类型结合使用。当设置Qt&#xff1a;&#xff1a;UniqueConnection时&#xff0c;如果连接已经存在&#xff08;即&#xff0c;如果同一信号已经连接到同一对对象的同一插槽&#xff09;

直接连接的情况&#xff1a;

当发射信号的线程和槽函数槽函数的线程相同时&#xff1a;

这种情况的弊端在于&#xff0c;主线程可以访问该函数&#xff0c;run函数也可以访问该函数&#xff0c;需要使用互斥锁来同步数据。

 线程&#xff1a; My_thread.cpp文件

#include "my_thread.h"
#include
My_thread::My_thread(QObject *parent) : QThread(parent)
{
}
void My_thread::run()//重写run函数
{
qDebug()<<"run中的线程号&#xff1a;"< exec();//开启事件循环
}
void My_thread::show_Slot_place()//槽函数
{
qDebug()<<"槽函数的线程号"<}

主类&#xff1a;Widget.cpp文件

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
qDebug()<<"主线程的位置&#xff1a;"< My_thread *thread&#61;new My_thread;
connect(this,&Widget::show_Slot,thread,&My_thread::show_Slot_place);//直接连接
thread->start();//开启线程
emit show_Slot();//触发信号
}

 当槽函数和线程和run&#xff08;&#xff09;函数相同时&#xff1a;

  • 在上面例子中的构造函数添加 movetoThread(this)&#xff08;不建议使用&#xff09;
  • 使用继承QObject &#xff0c;然后调用moveThread&#xff08;thread&#xff09;的方式创建

例子在文章开头已经介绍了&#xff0c;这里就不重复介绍了。 

 

同步线程和异步线程&#xff1a;

同步线程&#xff1a;

线程对象主动等待线程生命期结束后才销毁&#xff0c;线程对象销毁时确保线程执行结束&#xff0c;支持在栈或堆上创建线程对象。

在线程类的析构函数中先调用wait函数&#xff0c;强制等待线程执行结束。

使用场合&#xff1a;适用于线程生命期较短的场合

异步线程&#xff1a;

线程生命期结束时通知线程对象销毁。

只能在堆空间创建线程对象&#xff0c;线程对象不能被外界主动销毁。

在run函数中最后调用deleteLater()函数。

线程函数主动申请销毁线程对象。

使用场合&#xff1a;

线程生命期不可控&#xff0c;需要长时间运行于后台的线程。

 

 参考文章&#xff1a;

Qt 的线程与事件循环_FreyrLin的博客-CSDN博客

【QT】子类化QThread实现多线程_李春港的博客-CSDN博客

 QT多线程编程详解_coolboywjun的博客-CSDN博客_qt 多线程 




推荐阅读
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文由编程笔记小编整理,主要介绍了使用Junit和黄瓜进行自动化测试中步骤缺失的问题。文章首先介绍了使用cucumber和Junit创建Runner类的代码,然后详细说明了黄瓜功能中的步骤和Steps类的实现。本文对于需要使用Junit和黄瓜进行自动化测试的开发者具有一定的参考价值。摘要长度:187字。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
author-avatar
changeverything77_262
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有