Qt之线程运行指定函数(含源码+注释,优化速率)

news2024/9/24 11:20:49

一、线程示例图

下图包含三种不同方式启动线程的示例图和各自运行速率的对比;C++线程的启动方式包括阻塞运行和异步运行,可以从C++线程启动按钮看出两者区别,异步启动时按钮文本立即更新,当阻塞启动时按钮文本在线程运行完成后更新;并且从输出文本可以看出,启动的线程的线程ID和主线程ID不同,说明指定函数的确是在子线程中运行。
请添加图片描述

二、三种方式须知(个人理解)

2.1 C++线程

C++线程有不同的启动方式,分别是阻塞启动(join()函数)和异步启动(detach()函数);阻塞启动,顾名思义阻塞主线程,等待子线程运行完成后回到主线程中;异步启动,主线程和子线程分开各自运行,但是传输的数据不能是局部变量(当为局部变量时,主线程跳出局部变量作用域,该变量将释放,子线程有极大可能出问题),并且数据类型得为void *,在使用时自己转换即可。

2.2 MoveToThread

学习Qt的过程中,移动方法启动线程是我们刚刚接触线程时必了解的两种启动线程方法之一,其操作比其他两个方式略微麻烦(需要创建对象、关联信号、发送信号才能启动),具体看实际情况使用。

2.3 QtConcurrent

QtConcurrent中包含run的静态函数,可通过类名直接调用;该方法时三种方法中使用最方便的,只需要导入头文件、调用run函数,传入对应参数即可运行(详情见Qt帮助)。

三、源码

CTestClass.h

#ifndef CTESTCLASS_H
#define CTESTCLASS_H

#include <QObject>

typedef struct StData
{
    int range;  // 函数循环次数
    QString type; // 函数循环类型
    bool flag;  // 函数循环标记

    // 默认构造
    StData()
    {
        range = 0;
        type = "";
        flag = false;
    }

    // 有参构造
    StData(int range, QString type, bool flag = true)
    {
        this->range = range;
        this->type = type;
        this->flag = flag;
    }
}stData;


class CTestClass : public QObject
{
    Q_OBJECT
public:
    explicit CTestClass(QObject *parent = nullptr);

signals:

public slots:
    /**
     * @brief doSomethingOne 线程运行函数1
     * @param data 包含线程信息的数据
     */
    void doSomethingOne(const stData &data);
};

#endif // CTESTCLASS_H

CTestClass.cpp

#include "CTestClass.h"

#include <QThread>
#include <QDebug>
#include <QDateTime>

CTestClass::CTestClass(QObject *parent) : QObject(parent)
{

}

void CTestClass::doSomethingOne(const stData &data)
{
    // 获取线程启动时间
    qint64 startTime = QDateTime::currentMSecsSinceEpoch();
    for(int index = 0; index != data.range; ++index)
    {
        if(data.flag)
        {
            // 拼接信息字符串
            QString text = QString("线程ID:0x%1 启动类型:%2 函数名:%3 索引位置:%4")
                    .arg(QString::number((int64_t)QThread::currentThreadId(), 16))
                    .arg(data.type)
                    .arg(__func__)
                    .arg(QString::number(index));
            // 打印信息字符串
            qDebug() << text;
            // 延迟
            QThread::msleep(200);
        }
    }

    // 计算运行时间
    QString info = QString("线程类型:%1 本次线程运行时间:%2毫秒")
            .arg(data.type)
            .arg(QDateTime::currentMSecsSinceEpoch() - startTime);
    //! noquote():自动插入引号字符,并返回对流的引用(此处功能为使‘\n’生效,达到换行效果)
    qDebug().noquote() << info + '\n';
}

CFuncToThreadTest.h

#ifndef CFUNCTOTHREADTEST_H
#define CFUNCTOTHREADTEST_H

#include "CTestClass.h"

#include <QMainWindow>
#include <QThread>

namespace Ui {
class CFuncToThreadTest;
}

class CFuncToThreadTest : public QMainWindow
{
    Q_OBJECT

public:
    explicit CFuncToThreadTest(QWidget *parent = nullptr);
    ~CFuncToThreadTest();


signals:
    void moveToClassThreadSig(stData);

private slots:
    /**
     * @brief doSomethingTwo 线程运行函数2
     * @param data 包含线程信息的数据
     */
    void doSomethingTwo(const stData &data);

    /**
     * @brief on_qtConCurrentBtn_clicked QtConcurrent线程槽函数
     */
    void on_qtConCurrentBtn_clicked();

    /**
     * @brief on_otherMoveToBtn_clicked moveToThread槽函数
     */
    void on_otherMoveToBtn_clicked();

    /**
     * @brief on_cppThreadBtn_clicked C++线程槽函数
     */
    void on_cppThreadBtn_clicked();

    /**
     * @brief on_compareThreadBtn_clicked 比较各线程速率槽函数
     */
    void on_compareThreadBtn_clicked();

private:
    Ui::CFuncToThreadTest   *ui;

    QThread                 *m_moveClassThread; // 移动线程对象

    CTestClass              *m_testClass;   // 包含移动线程运行函数的对象

    StData                  *m_stData;  // C++线程指针数据
};

#endif // CFUNCTOTHREADTEST_H

CFuncToThreadTest.cpp

#include "CFuncToThreadTest.h"
#include "ui_CFuncToThreadTest.h"

#include <QDebug>
#include <thread>
#include <QtConcurrent/QtConcurrent>

#define COMPARE_RANGE_VAL 1000000000

/**
 * @brief doSomethingThree 线程运行函数3
 * @param dataPtr 包含线程信息的数据指针
 */
void doSomethingThree(void *dataPtr)
{
    // 获取线程启动时间
    qint64 startTime = QDateTime::currentMSecsSinceEpoch();
    // 将指针强转为stData类型
    stData *data = static_cast<stData *>(dataPtr);
    for(int index = 0; index != data->range; ++index)
    {
        if(data->flag)
        {
            // 拼接信息字符串
            QString text = QString("线程ID:0x%1 启动类型:%2 函数名:%3 索引位置:%4")
                    .arg(QString::number((int64_t)QThread::currentThreadId(), 16))
                    .arg(data->type)
                    .arg(__func__)
                    .arg(QString::number(index));
            // 打印信息字符串
            qDebug() << text;
            // 延迟
            QThread::msleep(200);
        }
    }

    // 计算运行时间
    QString info = QString("线程类型:%1 本次线程运行时间:%2毫秒")
            .arg(data->type)
            .arg(QDateTime::currentMSecsSinceEpoch() - startTime);
    //! noquote():自动插入引号字符,并返回对流的引用(此处功能为使‘\n’生效,达到换行效果)
    qDebug().noquote() << info + '\n';
}


CFuncToThreadTest::CFuncToThreadTest(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::CFuncToThreadTest)
    , m_stData(nullptr)
{
    ui->setupUi(this);
    // 拼接主线程ID
    ui->mainThreadIdLab->setText("主线程ID:0x" + QString::number((int64_t)QThread::currentThreadId(), 16));
    qRegisterMetaType<stData>("stData");
    qRegisterMetaType<stData>("const stData &");

    // 创建其他类对象
    m_testClass = new CTestClass;
    // 创建其他类使用的线程对象
    m_moveClassThread = new QThread;
    //! 链接启动槽函数
    //! 链接其他类线程信号和线程结束信号
    connect(this, &CFuncToThreadTest::moveToClassThreadSig, m_testClass, &CTestClass::doSomethingOne);
    connect(m_moveClassThread, &QThread::finished, m_testClass, &CTestClass::deleteLater);
    // 调用moveToThread添加线程
    m_testClass->moveToThread(m_moveClassThread);
    // 启动其他类线程
    m_moveClassThread->start();

    // 创建C++线程指针数据
    m_stData = new StData(5, "cppThread——异步运行");

}

CFuncToThreadTest::~CFuncToThreadTest()
{
    // 其他类线程退出
    m_moveClassThread->quit();
    m_moveClassThread->wait(1);
    delete m_stData;
    delete m_moveClassThread;
    delete ui;
}

void CFuncToThreadTest::doSomethingTwo(const stData &data)
{
    // 获取线程启动时间
    qint64 startTime = QDateTime::currentMSecsSinceEpoch();
    for(int index = 0; index != data.range; ++index)
    {
        if(data.flag)
        {
            // 拼接信息字符串
            QString text = QString("线程ID:0x%1 启动类型:%2 函数名:%3 索引位置:%4")
                    .arg(QString::number((int64_t)QThread::currentThreadId(), 16))
                    .arg(data.type)
                    .arg(__func__)
                    .arg(QString::number(index));
            // 打印信息字符串
            qDebug() << text;
            // 延迟
            QThread::msleep(200);
        }
    }

    // 计算运行时间
    QString info = QString("线程类型:%1 本次线程运行时间:%2毫秒")
            .arg(data.type)
            .arg(QDateTime::currentMSecsSinceEpoch() - startTime);
    //! noquote():自动插入引号字符,并返回对流的引用(此处功能为使‘\n’生效,达到换行效果)
    qDebug().noquote() << info + '\n';
}

void CFuncToThreadTest::on_qtConCurrentBtn_clicked()
{
    // 启动线程
    QtConcurrent::run(this, &CFuncToThreadTest::doSomethingTwo, stData(5, "QtConcurrent"));
}

void CFuncToThreadTest::on_otherMoveToBtn_clicked()
{
    // 发送其他类线程信号
    emit moveToClassThreadSig(stData(5, "OtherMoveToThread"));
}

void CFuncToThreadTest::on_cppThreadBtn_clicked()
{
    // 创建静态标记值(作用为交替调用线程启动方式)
    static bool flag = false;

    // 赋值的同时改值,并使用赋值后的变量判断
    if(flag = !flag)
    {
        //! 此处不能传入局部变量
        //! 线程为异步运行,此处传入局部变量,局部变量将释放

        // 创建线程对象并传入数据
        std::thread thread(doSomethingThree, m_stData);
        // 线程异步启动
        thread.detach();

        // 更改按钮文本
        ui->cppThreadBtn->setText("C++_Thread_阻塞启动");
    }
    else
    {
        //! 可传入局部变量
        //! 线程为阻塞运行,此处传入局部变量,局部变量不会释放
        // 创建线程数据
        stData data(5, "cppThread——阻塞运行");
        // 创建线程对象,并传入数据
        std::thread thread(doSomethingThree, &data);
        // 线程阻塞启动
        thread.join();

        // 更改按钮文本
        ui->cppThreadBtn->setText("C++_Thread_异步启动");
    }
}

void CFuncToThreadTest::on_compareThreadBtn_clicked()
{
    // 打印分割线
    qDebug() << "======================================";

    /// moveToThread
    // 发送信号启动线程
    emit moveToClassThreadSig(stData(COMPARE_RANGE_VAL, "OtherMoveToThread", false));

    /// QtConcurrent
    QtConcurrent::run(this, &CFuncToThreadTest::doSomethingTwo, stData(COMPARE_RANGE_VAL, "QtConcurrent", false));

    /// C++线程
    // 标记值更新为false,防止进入if
    m_stData->flag = false;
    // 更新循环次数为宏定义值
    m_stData->range = COMPARE_RANGE_VAL;
    // 设置线程类型文本
    m_stData->type = "cppThread——阻塞运行";
    // 创建c++线程并运行
    std::thread thread(doSomethingThree, m_stData);
    // 线程阻塞启动
    thread.detach();
}

总结

总所周知,当程序运行卡顿,速率不理想时,可通过线程提高运行速率;本文提到的三种方式在想要提高速率的情况时还是蛮有用的,当然具体使用那种方式还得看实际情况。

相关文章

启动QThread线程的两种方法(含源码+注释)
Qt互斥锁(QMutex)、条件变量(QWaitCondition)讲解+QMutex实现多线程循环输出ABC(含源码+注释)
Qt互斥锁(QMutex)的使用、QMutexLocker的使用(含源码+注释)
QSemaphore的使用+QSemaphore实现循环输出ABC(含源码+注释)
QRunnable线程、QThreadPool(线程池)的使用(含源码+注释)
Qt读写锁(QReadWriteLock)的使用、读写锁的验证(含源码+注释)
Qt读写锁(QWriteLocker、QReadLocker)的理解和使用(含部分源码)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除

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

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

相关文章

mybatis处理返回结果集

结果处理 1 简单类型输出映射 返回简单基本类型 //查询管理员总数 int adminCount(); <select id"adminCount" resultType"int">select count(*) from admin </select> 返回结果需要定义后才能使用简称 eg&#xff1a;resultType"Adm…

个人信息保护合规建设桔皮书

声明 本文是学习个人信息保护合规建设桔皮书. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 个人信息保护建设思考 识别监管要求聚焦安全保护重点 1. 个人信息处理原则合规 《个人信息保护法》总则中规定了个人信息处理的合法、正当、必要…

应用torchinfo计算网络的参数量

1 问题定义好一个VGG11网络模型后&#xff0c;我们需要验证一下我们的模型是否按需求准确无误的写出&#xff0c;这时可以用torchinfo库中的summary来打印一下模型各层的参数状况。这时发现表中有一个param以及在经过两个卷积后参数量&#xff08;param&#xff09;没变&#x…

从socket开始讲解网络模式(epoll)

从socket开始讲解网络模式 windows采用IOCP网络模型&#xff0c;而linux采用epoll网络模型&#xff08;Linux得以实现高并发&#xff0c;并被作为服务器首选的重要原因&#xff09;&#xff0c;接下来讲下epoll模型对网络编程高并发的作用 简单的socket连接 socket连接交互的…

Python学习笔记-PyQt6之MVC项目结构初试

MVC结构是之model-view-controller三层架构的开发框架&#xff0c;用以将项目界面和逻辑进行解耦分析&#xff0c;便于维护。与WPF的MVVM相似。 项目开发做了一个秒表试手&#xff1a; 1.项目架构如下 controller&#xff1a;用于放置界面的控制逻辑model&#xff1a;用于放置…

回顾这十年,感悟

十年前&#xff0c;我35岁了&#xff0c;在体制内工作&#xff0c;到了很多人眼里的躺平的年龄。我眼里的世界&#xff0c;也就那么大&#xff0c;没有想过更进一步&#xff0c;有点中年油腻了&#xff0c;体质也差了。……终于有一天&#xff0c;醒悟了&#xff0c;不想过这样…

高并发系统设计 -- 秒杀系统

高并发秒杀 秒杀问题相信大家都知道的&#xff0c;虽然是一个烂大街的项目&#xff0c;但是秒杀问题背后的知识是很值得学习的&#xff0c;很多高并发系统设计都可以参照秒杀系统来进行实现。而且顺着这个问题&#xff0c;我会教给大家如何进行高并发的系统设计。 我们先来看…

Android集成三方浏览器之Crosswalk

上一篇讲解了腾讯 X5 内核的集成&#xff0c;这一篇是讲解 Crosswalk 的集成 Crosswalk 也是采用了Chromenium 内核&#xff0c;是一款开源的 web 引擎&#xff0c;开发者可以直接把 Crosswalk 嵌入到应用之中&#xff0c;当然也支持共享模式&#xff08;系统中没有对应的 Cros…

费解的开关(BFS+哈希表+二进制枚举)

费解的开关&#xff08;BFS哈希表二进制枚举&#xff09;一、题目二、思路分析1、算法标签2、思路梳理方法1&#xff1a;BFS哈希表方法2&#xff1a;二进制枚举DFS一、题目 二、思路分析 1、算法标签 这道题考察的是BFS哈希表,DFS二进制枚举 2、思路梳理 方法1&#xff1a;…

Cohen–Sutherland 算法介绍(简单易懂)

目录 一、算法介绍 二、算法描述 三、算法总结 一、算法介绍 Cohen–Sutherland 算法用于直线段裁剪&#xff0c;通过判断直线与窗口之间的关系&#xff0c;来决定直线段部分的保留与舍弃。 二、算法描述 ① 首先&#xff0c;我们把屏幕分割成 9 个区域块&#xff0c;最中间区…

音乐相册如何制作?一步一步教会你

很多小伙伴会在旅行时&#xff0c;拍摄各种好看的照片&#xff0c;一趟旅途下来能留下好多照片呢&#xff0c;有些人会习惯将这些照片归类到一个相册里。其实我们也可以使用一些免费的软件将这些照片制作成有纪念意义的音乐相册&#xff0c;那大家知道免费制作音乐相册怎么做吗…

npm install 报警告npm WARN

npm install 报警告npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents1.2.0 (node_modules\fsevents npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN fsevents1.2.0 had bundled packages that do not match the requi…

Crack:Inobitec DICOM Viewer Pro 2.9 多语言版本

Inobitec DICOM Viewer Pro 的使命是扩大医生可见和可能的范围。通过为医学提供高质量的创新 IT 解决方案&#xff0c;Ω578867473为改善全世界人民的健康做出了贡献。感受到自己工作的价值&#xff0c;意识到 21 世纪医学面临的挑战的重要性&#xff0c;以及解决这些挑战的乐趣…

WordPress使用二级域名存储图片等静态资源达到网站加速的详细配置

最近发现源站压力较大&#xff08;水管太小&#xff09;于是想着把WordPress博客的图片等静态资源分离到二级域名中&#xff0c;二级域名再使用一次云盾免费加速CDN&#xff0c;达到动静分离的效果&#xff0c;在这个过程中遇到一些坑&#xff0c;特此记录一下&#xff0c;方便…

NumpyPandas 数据处理与挖掘

笔记来源B站&#xff1a;https://www.bilibili.com/video/BV1xt411v7z9?p21 python学习笔记1 Numpy1.1 Numpy优势1.1.1 Numpy介绍1.1.2 ndarray介绍1.1.3 ndarray与Python原生list效率对比1.1.4 ndarray优势1.2 认识N维数组-ndarray属性1.2.1 ndarray的属性1.2.2 ndarray的形状…

11.1、基于Django4的可重用、用户注册和登录系统搭建

文章目录系统的功能思路分析搭建项目环境创建项目&#xff08;虚拟环境&#xff09;创建子应用修改语言、时区创建数据库表启动项目git提交项目代码到本地仓库git initi 初始化&#xff0c;创建本地git仓库pycharm安装 .ignore插件&#xff0c;来设置git的忽略文件提交代码修改…

SpringBoot+VUE前后端分离项目学习笔记 - 【09 SpringBoot集成MyBatis-Plus和SwaggerUI】

集成mybatis-plus依赖 官网 : https://baomidou.com/ pom.xml <!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></depe…

01月份图形化一级打卡试题

活动时间 从2023年 1月1日至1月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; &#xff08;1&#xff09;小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 &#xff08;2&#xff09;小朋友做完题目后&…

认证的未来:2023 年值得关注的四大趋势

在经济不确定性和地缘政治紧张的一年中&#xff0c;数字领域充满网络威胁也就不足为奇了。从广泛的假冒诈骗到日益增多的短信网络钓鱼&#xff0c;网络攻击的频率和严重程度在 2022 年有所增加&#xff0c;这突显了所有行业的组织身份验证漏洞。 因此&#xff0c;当我们翻开新…

amis组件学习的配置介绍(二)

table view 表格视图 这个看文档也很好理解&#xff0c;但是还是需要介绍一下。 trs&#xff1a; <Array>设置表格行属性。tds: <Array>设置单元格属性。 {"type": "table-view",// 设置表格行"trs": [{"background": &…