Qt常用的多线程使用方式

目前(Qt5)常用的多线程的方式?

        1、派生于QThread然后重写run()函数

        2、通过将派生QObject的类对象通过moveToThread()来移动到新的线程中

        3、通过inherit QRunnable类然后重写run()方法、然后借助QThreadPool线程池来实现多线程

        4、通过高级语法 QtConcurrent模块来实现多线程

        

本文主要讲解不同多线程的使用方式,并穿插不同之处和注意事项,方便后来人的学习

        在开始之前,我们需要先明确几个概念:对象和线程。对象指的是派生于QObject以及QThread类的实例化对象,线程指的是多线程对象开辟出的新线程,这个线程和主线程是两个并行存在。希望不要将对象和线程搞混了。

一、派生于QThread然后重写run()函数

        这种方式是使用比较传统的方式,直接上一个简单的demo:       

#include <QThread>class Thread: public QThread
{
public:Thread(QObject* parent=nullptr);signals:void signalNotify();public slots:void receiveMesg();protected:void run(){//do something}}///   widget.cpp  ///
#include "Thread.h"
#include <QApplication>
Widget::Widget(QWidget* parent)
{Thread *t = new Thread();t->start(); 
}

        这是最基础的用法和结构,派生于QThread、重写run()函数、创建线程对象以及开启线程。在这种方式下,耗时操作都仍给了run()函数,所以如果需求复杂一些,就需要在run()中实现具体的业务。但是有几点我们需要注意:

        1)不要在多线程中直接操作UI

        2)正确管理、使用定时器等资源

      既然不能在run中直接操作UI,那我们要是想把逻辑运算的结果通知到UI又该怎么操作呢?通过信号槽的方式。这里要注意的是:在主线程中创建线程对象后,比如上面Thread实例化对象,这个线程对象是属于主线程的,所以在主线程中使用信号曹将线程对象和GUI对象连接起来后,这并不是多线程通信,真正子线程部分的是在run()中的逻辑

        此外,如果想在子线程中使用定时器,一定要在run()中创建,停止也要在子线程中操作,切莫跨线程操作定时器。而且,在run()中创建的资源都是属于子线程的,对这些资源的操作一定要注意。在run()中连接的信号槽也是属于子线程的。

那想要在QThread中使用信号槽,仅在run()中绑定信号槽就行了吗?

        不是,必须在run()中通过调用exec()来开启事务循环。只有开启事务循环,那么依赖于事务循环的种种特性:定时器、信号槽、TCP通信、网络请求以及各种QEvent等才能使用,明白了吧?要是用不到上面那些特性,只想执行一些耗时操作,那么能不能不加exec()?要是不加的话,子线程在运行完耗时逻辑后就会结束线程。        

        在代码中看到在实例化Thread对象指针的时候没有指定parent,那能指定parent吗?不能,为什么创建QThread派生类对象时候不能指定parent?一方面源代码的实现中要求不能这么做,如下:       

void QObject::moveToThread(QThread *targetThread)
{Q_D(QObject);if (d->threadData->thread.loadAcquire() == targetThread) {// object is already in this threadreturn;}if (d->parent != 0) {qWarning("QObject::moveToThread: Cannot move objects with a parent");return;}if (d->isWidget) {qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");return;}//do something else.....
}

        还有就是存在潜在的风险,如果指定了parent,那么一旦parent生命周期结束了,那势必要回收parent占用的资源,这里面包括QThreadChild对象占用的资源。但是此时子线程很可能正在干活,人家正在吃饭呢,你把桌子掀了,我想乌鸦也不会答应吧?

        既然没有指定parent就不能借助Qt的半自动内存回收机制,那就需要人为的手动删除内存,即通过QThread::finish信号来连接QThreadChild::deleteLater函数来实现对象资源的释放

二、通过将派生QObject的类对象通过moveToThread()来移动到新的线程中

        这种方式适合于业务比较明确划分的情况,通过将一类业务单独抽离成一个类,然后将类的业务响应在多线程中执行。下面先上一个demo:       

#include <QObject>class Work: public QObject
{Q_OBJECTpublic:Work(QObject* parent=nullptr);signals:void sigSendMsg(const QString&);public slots:void receiveMsg(const QString& msg);
}Widget::Widget(QWidget* parent)
{mWork = new Work;connect(mWork, &Work::sigSendMsg, this, &Widget::slotFunc);workThread = new QThread;    connect(workThread, &QThread::finish, mWork, &Work::deleteLater);connect(workThread, &QThread::finish, workThread, &QThread::deleteLater);mWork->moveToThread(workThread)workThread->start();
}Widget::~Widget()
{workThread->quit();workThread->wait();
}

        这种方式的核心就是moveToThread(),moveToThread移动了什么?是线程对象的归属权吗?No!是将线程对象中的槽函数放在了新线程中执行,而线程对象依然属于创建它所在的线程中。切莫以为执行了moveToThread后线程对象所有的一切都打包给新线程了。在哪创建就属于哪,在多线程中依然适用。使用moveToThread方式时、对象不能设置parent,不然无法完成移动。

        既然moveToThread也是借助于QThread,那么如果此时有一个inherit QThread的子类ThreadA,以及通过move方式到ThreadA中的线程B,那这两个线程run()和线程B谁先执行呢?通过测试发现,run()先执行,执行完run()后再执行move进来的槽函数。

三、通过inherit QRunnable类然后重写run()方法、然后借助QThreadPool线程池来实现多线程

        直接上demo:       

  class HelloWorldTask : public QRunnable{void run() override{qDebug() << "Hello world from thread" << QThread::currentThread();}};HelloWorldTask *hello = new HelloWorldTask();// QThreadPool takes ownership and deletes 'hello' automaticallyQThreadPool::globalInstance()->start(hello);

        这种方式的核心并不是如何使用,而是了解线程池。线程池里有多少个正在干活的线程activeThreadCount?这个池子又能放下多少个线程maxThreadCount?要是现在没有多余的线程能够用、那么被丢进池子里的多线程任务又是如何处理的?看QThreadPool了解。

开启多少个线程合适呢?

        线程的开辟和切换需要消耗CPU资源的,尤其是涉及到CPU的上下文切换,所以并不是线程开的越多越好,那多少是理想值呢?一般根据业务要求来,有的是内核数量的4倍,有的高达16倍。根据QThreadPool::maxThreadCount()来看,这个于计算机的real and logic cores数量相关

四、通过高级语法 QtConcurrent模块来实现多线程

        这种方式就很简单了,适用于做一些纯属于简单的累活,干完就拉到,中间不需要交互过程,一般都是配合lambda表达式使用,用的时候别忘了在.pro中添加QT += concurrent       

QtConcurrent::run();

        

拓展内容:

        关于currentThreadId(),正确获取多线程id的方式:

#include <QCoreApplication>
#ifdef Q_OS_LINUX#include <pthread.h>
#endif#ifdef Q_OS_WIN#include <windows.h>
#endifint main()
{#ifdef Q_OS_LINUXqDebug()<<pthread_self();#endif#ifdef Q_OS_WINqDebug()<<GetCurrentThreadId();#endif
}

        多线程也是有优先级,可以通过QThread::setPriority()来设置

                此外,还提供了QThread::isInterruptionRequested()来判断是否可以提前跳出线程循环:

while(true)
{if(!isInterruptionRequested()){//耗时操作}
}

写在最后:

        上面介绍了常用的多线程方式,那实际的工作中还有一个技巧,就是不通过信号槽的方式在主线程中仍然能调用子线程函数的方式:QMetaObject::invokeMethod,参数可以指定是跨线程调用还是直接在同线程中调用

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

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

相关文章

JVM内存回收算法

1.1 引用计数法 每个对象创建的时候&#xff0c;会分配一个引用计数器&#xff0c;当这个对象被引用的时候计数器就加1&#xff0c;当不被引用或者引用失效的时候计数器就会减1。任何时候&#xff0c;对象的引用计数器值为0就说明这个对象不被使用了&#xff0c;就认为是“垃圾…

奇舞周刊第521期:“一切非 Rust 项目均为非法”

奇舞推荐 ■ ■ ■ 拜登&#xff1a;“一切非 Rust 项目均为非法” 科技巨头要为Coding安全负责。这并不是拜登政府对内存安全语言的首次提倡。“程序员编写代码并非没有后果&#xff0c;他们的⼯作⽅式于国家利益而言至关重要。”白宫国家网络总监办公室&#xff08;ONCD&…

在idea中用模板骨架初始创建maven管理的web项目时没有src有关的目录的解决方案

一.问题如下 二.解决方法 首先关闭当前项目&#xff0c;接着修改全局设置&#xff0c;重新创建项目 在VM Options中添加"-DarchetypeCataloginternal"&#xff0c;点击ok保存 点击创建&#xff0c;如果创建成功没报错且有src&#xff0c;就ok了。 当然如果出现以下…

「媒体宣传」如何写好新闻稿?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 写好新闻稿是媒体宣传的关键环节之一&#xff0c;下面是一些关于如何写好新闻稿的建议&#xff1a; 明确新闻稿的目的和受众&#xff1a;在写新闻稿之前&#xff0c;首先要明确新闻稿的目…

仿牛客网项目---帖子详情功能的实现

这篇文章主要讲讲帖子详情功能。其实帖子详情功能简单来说就是你点进去可以看到文章&#xff0c;这就叫帖子详情功能。那接下来我讲讲我的这个项目是如何实现这个功能的。 首先写DAO层。 Mapper public interface DiscussPostMapper {List<DiscussPost> selectDiscussPo…

StarRocks实战——表设计规范与监控体系

目录 前言 一、StarRocks表设计 1.1 字段类型 1.2 分区分桶 1.2.1 分区规范 1.2.2 分桶规范 1.3 主键表 1.3.1 数据有冷热特征 1.3.2 大宽表 1.4 实际案例 1.4.1 案例一&#xff1a;主键表内存优化 1.4.2 案例一&#xff1a;Update内存超了&#xff0c;导致主键表导…

AI推荐算法的演进之路

推荐算法 基于大数据和AI技术&#xff0c;提供全流程一站式推荐平台&#xff0c;协助企业构建个性化推荐应用&#xff0c;提升企业应用的点击率留存率和永久体验。目前&#xff0c;主要的推荐方法包括&#xff1a;基于内容推荐、协同过滤推荐、基于关联规则推荐、基于效用推荐…

基于阿里云平台 通过树莓派实现 1:1人脸识别

之前的学习中&#xff0c;曾经在香橙派上使用阿里云平台的服务实现过类型识别&#xff1a; 使用香橙派并基于Linux实现最终版智能垃圾桶项目 --- 下_香橙派 项目-CSDN博客 现在&#xff0c;尝试在树莓派上通过阿里云平台的服务实现人脸识别&#xff01; 通过VScode远程连接树莓…

2024年新提出的算法|鹦鹉优化器(Parrot optimizer):算法及其在医疗问题中的应用

本期介绍一种基于训练后鹦鹉关键行为的高效优化方法——鹦鹉优化器(Parrot Optimizer, PO)。该成果于2024年2月发表在中科院2区top SCI期刊Computers in Biology and Medicine&#xff08;IF7.7&#xff09; 1、简介 鹦鹉优化器&#xff08;PO&#xff09;是一种受训练有素的…

pytest教程-13-conftest.py文件

上一小节我们学习了fixture的作用域&#xff0c;本小节我们学习一下pytest conftest.py文件的使用方法。 conftest.py文件的作用 conftest.py文件是pytest框架中的一个特殊文件&#xff0c;用于定义共享的设置、夹具(fixture)和钩子函数&#xff08;hook&#xff09;。 在py…

2.模拟问题——2.使用二维数组输出图形

用二维数组描述图形 首先要计算出整个输出的方框大小&#xff0c;从而判定相应关键循环点 #include <cstdio> char arr[1000][3000]; int main() {int h;//初始化&#xff0c;全部内部填空格while(scanf("%d",&h) ! EOF){for (int i 0; i < h; i) {f…

HTML---表单验证

文章目录 目录 本章目标 一.表单验证概述 二.表单选择器 属性过滤选择器 三.表单验证 表单验证的方法 总结 本章目标 掌握String对象的用法会使用表单选择器的选择页面元素会使用JQuery事件进行表单验证Ajax的概念和作用 一.表单验证概述 前端中的表单验证是在用户提交表…

图神经网络导论 - 刘知远

一、神经网络基础 近年来&#xff0c;机器学习领域的发展迅速&#xff0c;主要表现在多种神经网络架构的出现。尽管不同的神经网络架构相差甚远&#xff0c;但现有的神经网络架构可以分为几个类别&#xff1a; 卷积神经网路是前馈神经网路的特殊形式&#xff0c;FNN通常是全…

什么是VR虚拟现实|虚拟科技博物馆|VR设备购买

虚拟现实&#xff08;Virtual Reality&#xff0c;简称VR&#xff09;是一种通过计算机技术模拟出的一种全新的人机交互方式。它可以通过专门的设备&#xff08;如头戴式显示器&#xff09;将用户带入一个计算机生成的虚拟环境之中&#xff0c;使用户能够与这个虚拟环境进行交互…

Spring Boot文档目录

目录 官方文档 说明&#xff1a;本文档翻译的版本&#xff1a;2.7.18-SNAPSHOT。 1. 法规&#xff08;Legal&#xff09; 2. 获取帮助&#xff08;Getting Help&#xff09; 3. 文档概述&#xff08;Documentation Overview&#xff09; 4. 开始使用&#xff08;Getting Sta…

BUUCTF---另外一个世界1

1.这是一道杂项题&#xff0c;也是我觉得最值得记录的一道题。 2.话不多说&#xff0c;题目描述&#xff08;真的是另一个世界&#xff09; 3.下载附件&#xff0c;是一张图片 4.尝试了查看属性&#xff0c;以及在记事本中打开看看有没有什么有用的信息&#xff0c;发现没什么…

FaceBook获取广告数据

1、访问 广告管理工具 确认自己登陆的账号下面能看到户。 ​ 2、使用 图谱Api探索工具 生成用户短期口令 ​ 3、get请求(或者浏览器直接打开)访问&#xff1a; https://graph.facebook.com/v19.0/me?fieldsid,name, email&access_token{上一步生成的口令} ​ 4、短期…

c# 获取源码路径与当前程序所在路径

获取源码路径 private static string GetFilePath([CallerFilePath] string path null) {return path;}//当程序所在路径string str67 System.Environment.CurrentDirectory;//源码路径 var path GetFilePath();var directory Path.GetDirectoryName(path);参考

Vue2:用node+express写一个轻量级的后端服务

1、桌面创建demo文件夹 进入demo&#xff0c;执行如下命令 npm init输入名称&#xff1a; test_server然后一路回车 2、安装express框架 npm i express3、新建server.js 在demo文件夹中&#xff0c;新建server.js const express require(express) const app express()…

2023年12月CCF-GESP编程能力等级认证Scratch图形化编程三级真题解析

一、单选题(共15题,共30分) 第1题 现代计算机是指电子计算机,它所基于的是( )体系结构。 A:艾伦图灵 B:冯诺依曼 C:阿塔纳索夫 D:埃克特-莫克利 答案:B 第2题 默认小猫角色,执行下列程序,舞台上会看到? ( ) A: B: C: D: 答案:C