深入理解Qt多线程编程(QThreadPool)

news2025/1/31 20:43:34

多线程编程在现代软件开发中变得越来越重要,它能够提高应用程序的响应速度和处理性能。在Qt框架中,QThreadPool作为线程池管理工具,被频繁的使用。

目录

概述
接口介绍
底层原理解析
使用方法

概述

QThreadPool是Qt提供的一个线程池实现,用于管理和复用线程。线程池通过复用现有的线程来避免频繁创建和销毁线程带来的性能开销,适用于需要频繁执行并发任务的场景。QThreadPool内部维护了一个线程队列,可以在程序运行时动态分配和管理线程资源。

接口介绍

int activeThreadCount() const

返回当前活动(正在运行任务)线程的数量。这个数字不包括闲置等待工作的线程。

void clear()

清除线程池的任务队列。已经开始的任务将会继续完成,但队列中等待开始的任务会被取消。

bool contains(const QThread *thread) const

检查指定的线程是否属于该线程池。

int expiryTimeout() constvoid setExpiryTimeout(int expiryTimeout)

返回/设置线程过期时间,单位是毫秒。
线程在完成任务后,如果在过期时间内没有心的任务分配给它,那么它将被销毁。
默认expiryTimeout值为30000毫秒(30秒)。

注意:
设置expiryTimeout对已运行的线程没有影响。只有新创建的线程才会使用新的expiryTimeout。我们建议在创建线程池之后,调用start()之前设置expiryTimeout

int maxThreadCount() constvoid setMaxThreadCount(int maxThreadCount)

返回/设置线程池中允许的最大线程数。

注意:
maxThreadCount不能为零和负数,线程池至少有一个线程。

void releaseThread()void reserveThread()

reserveThread()方法的字面意思是保留线程(或者是预留线程),这里的保留线程不是指保留线程池中的线程不被销毁。该方法就是增加activeThreadCount()返回值(活动线程的数量)。
reserveThread()提供了一种机制,使得开发者可以更精细地控制线程的并行度和资源占用。通过通知线程池外部线程的存在,它帮助线程池避免在CPU资源已经被占用时过渡创建或激活线程,从而优化了资源的使用和任务的执行效率。

假设最理想的线程数量是10,线程池的maxThreadCount设置为10,但在线程池外还有1个GUI线程、4个独立任务线程,当线程池满负荷工作时,程序的线程数量将达到15个线程,超出了合理的线程数,导致一系列的性能问题。所以,为了更精细地控制线程数量,当线程池外的线程都工作时,调用线程池的reserveThread()来占用线程,使线程池只能有5个线程同时工作。

如果所有线程都在工作,调用该方法后会导致activeThreadCount()暂时大于maxThreadCount()
releaseThread()方法是释放之前通过reserveThread()方法预留的线程。

uint stackSize() constvoid setStackSize(uint stackSize)

获取/设置线程池工作线程的堆栈大小。
默认值为0,即工作线程的堆栈大小使用操作系统默认的堆栈大小。

注意:
设置堆栈大小时只对后续新创建的线程有用,对已创建或正在运行的线程没有影响。

void start(QRunnable *runnable, int priority = 0)

从线程池中拿出一个空闲的线程来运行runnable,如果当前线程池没有空闲的线程,那么runnable将被添加到运行队列中。

示例:
class MyTask : public QRunnable {
public:
    void run() override {
        qDebug() << "QRunnable Task is running in thread" << QThread::currentThread();
        // 模拟任务处理
        QThread::sleep(2);
        qDebug() << "QRunnable Task completed in thread" << QThread::currentThread();
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    QThreadPool *threadPool = QThreadPool::globalInstance();
    threadPool->setMaxThreadCount(3); // 设置最大线程数

    for (int i = 0; i < 5; ++i) {
        MyTask *task = new MyTask();
        threadPool->start(task);
    }

    threadPool->waitForDone(); // 等待所有任务完成

    return app.exec();
}

void start(std::function<void()> functionToRun, int priority = 0)

从线程池中拿出一个空闲的线程来运行functionToRun,如果当前线程没有空闲的线程,那么functionToRun将被添加到运行队列中。

示例:
void myFunctionTask() {
    qDebug() << "std::function Task is running in thread" << QThread::currentThread();
    // 模拟任务处理
    QThread::sleep(2);
    qDebug() << "std::function Task completed in thread" << QThread::currentThread();
}

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    QThreadPool *threadPool = QThreadPool::globalInstance();
    threadPool->setMaxThreadCount(3); // 设置最大线程数

    for (int i = 0; i < 5; ++i) {
        threadPool->start(std::function<void()>(myFunctionTask));
    }

    threadPool->waitForDone(); // 等待所有任务完成

    return app.exec();
}

bool tryStart(QRunnable *runnable)bool tryStart(std::function<void()> functionToRun)

尝试从线程池中拿出空闲的线程来运行runnablefunctionToRun
如果调用时,没有空闲的线程,则此函数直接返回false;否则,直接拿出空闲线程来运行,并返回true

示例:
class MyTask : public QRunnable {
public:
    void run() override {
        qDebug() << "Task is running in thread" << QThread::currentThread();
        // 模拟任务处理
        QThread::sleep(2);
        qDebug() << "Task completed in thread" << QThread::currentThread();
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    QThreadPool *threadPool = QThreadPool::globalInstance();
    threadPool->setMaxThreadCount(2); // 设置最大线程数

    for (int i = 0; i < 5; ++i) {
        MyTask *task = new MyTask();
        if (!threadPool->tryStart(task)) {
            qDebug() << "Failed to start task" << i << "due to lack of available threads";
            delete task; // 必须手动删除未能启动的任务以防止内存泄漏
        }
    }

    threadPool->waitForDone(); // 等待所有任务完成

    return app.exec();
}

bool tryTake(QRunnable *runnable)

尝试从线程池的等待队列中移除一个已经提交但尚未开始执行的runnable任务。
返回true表示移除成功,runnable对象的所有权将转移给调用者(即使runnable->autoDelete() == true)。

注意:
如果runnable->autoDelete() == true,调用tryTake()可以会删除错误的runnable
比如runnable指针指向的任务执行完成后,被自动销毁了,但后面又在同一块内存分配了新的runnable,然后调用tryTake()时删除的是新的任务对象。
所以官方建议:只对runnable->autoDelete() == false的任务对象调用此函数。

bool waitForDone(int msecs = -1)

最多等待msecs毫秒,让所有线程退出并从线程池中删除所有线程。如果所有线程都被删除,则返回true
如果msecs为默认值-1,则忽略超时,直到最后一个线程退出。

底层原理解析

线程状态

QSet<QThreadPoolThread *> allThreads;
QQueue<QThreadPoolThread *> waitingThreads;
QQueue<QThreadPoolThread *> expiredThreads;
QVector<QueuePage*> queue;

上面代码是QThreadPoolPrivate类的部分成员。

  • QSet<QThreadPoolThread *> allThreads

allThreads存储线程池中所有的线程对象,管理所有线程的生命周期、状态转换和清理工作。

  • QQueue<QThreadPoolThread *> waitingThreads

waitingThreads存储当前处于等待状态的线程对象,用于管理空闲线程,当有新任务到达时,可以从waitingThreads队列中取出一个线程来执行任务,从而避免频繁创建和销毁线程。

  • QQueue<QThreadPoolThread *> expiredThreads

expiredThreads存储当前处于过期状态的线程对象,用于管理过期线程,过期线程可能会被销毁以释放资源。

  • QVector<QueuePage*> queue

queue存储任务队列,按优先级管理任务,新任务会按优先级插入到适当的位置,以便高优先级的任务能够优先被执行。

小结:

所以,QThreadPool中的线程状态有:活动状态、空闲状态、过期状态。

  • 活动状态

活动状态指的是当前正在执行任务的线程状态,当前状态的线程对象存储在allThreads队列中。

  • 空闲状态

任务执行结束后线程进入空闲状态,在expiryTimeout过期时间内,线程对象将一直存储在waitingThreads队列中。直到超过过期时间后,线程将转为过期状态。

  • 过期状态

过期状态的线程会被批量释放。

线程池工作流程

添加任务流程:

未命名文件 (9).png

线程池工作流程:

未命名文件 (10).png

使用方法

子类化QRunnable

使用QThreadPool需要将子类化QRunnable作为线程任务对象。

class SimpleTask : public QRunnable {
protected:
    void run() override {
        qDebug() << "Simple task is running in thread" << QThread::currentThreadId();
        QThread::sleep(2); // 模拟线程任务耗时
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    SimpleTask *task = new SimpleTask();
    QThreadPool::globalInstance()->start(task);
    QThreadPool::globalInstance()->waitForDone(); // 等待所有线程结束

    return a.exec();
}

无参函数作为线程函数

void performTask()
{
    qDebug() << "Performing task in thread" << QThread::currentThreadId();
}

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    QThreadPool::globalInstance()->start(performTask);
    return a.exec();
}

带参函数作为线程函数

void performTask(int id)
{
    qDebug() << "Performing task in thread" << id << QThread::currentThreadId();
}

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    QThreadPool::globalInstance()->start(std::bind(performTask, 2));
    return a.exec();
}

Lambda表达式作为线程函数

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    QThreadPool::globalInstance()->start([]() {
        qDebug() << "Lambda task running in thread" << QThread::currentThreadId();
    });
    return a.exec();
}

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

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

相关文章

钢结构厂房降温方案

钢结构厂房降温方案 这种钢结构的厂房的车间大&#xff0c;天就晒透了&#xff0c;然后你的工作往往外温度又高&#xff0c;你前面又弄个大火炉烤你身上&#xff0c;你能不热吗&#xff1f;你热怎么办&#xff1f;风扇你就是在25左右的时候吹着挺舒服的&#xff0c;因为它毕竟…

179.二叉树:合并二叉树(力扣)

代码解决 /*** 二叉树节点的定义。* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, Tre…

Pytorch--Convolution Layers

文章目录 1.nn.Conv1d2.torch.nn.Conv2d()3.torch.nn.ConvTranspose1d()3.torch.nn.ConvTranspose2d() 1.nn.Conv1d torch.nn.Conv1d() 是 PyTorch 中用于定义一维卷积层的类。一维卷积层常用于处理时间序列数据或具有一维结构的数据。 构造函数 torch.nn.Conv1d() 的语法如…

如何用Vue3构建一个交互式音乐播放器

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Vue.js 开发音乐播放器卡片 应用场景 这款音乐播放器卡片旨在为音乐应用程序提供一个现代而交互式的用户界面。它包含诸如歌曲信息、播放进度条和控制按钮等关键功能。 基本功能 **歌曲信息显示&#xff1a…

Xinstall:解决App安装跳转难题,让你的用户流畅体验不再受阻

在互联网流量红利逐渐衰退的今天&#xff0c;App推广和运营面临着前所未有的挑战。如何确保在多变的互联网环境下&#xff0c;迅速搭建起能时刻满足用户需求的运营体系&#xff0c;已经成为众多企业急待解决的问题。今天&#xff0c;我们就来探讨一下如何通过Xinstall的安装跳转…

软件工程期末复习题

目录 选择 判断 选择 下列说法中正确的是 ( B )。 A、20 世纪50 年代提出了软件工程的概念摇 B、20 世纪60 年代提出了软件工程的概念 C、20 世纪70 年代出现了客户端/ 服务器技术 D、20 世纪80 年代软件工程学科达到成熟 软件危机的主要原因是 ( D )。 A、软件工具落后…

电子画册制作与传统画册相比,有哪些优势?

在当今数字化时代&#xff0c;电子画册作为一种新兴的媒体形式&#xff0c;其制作与传统画册相比具有显著的优势。以下是对这些优势的详细探讨。 首先&#xff0c;电子画册的制作过程通常更加便捷和经济。相较于传统画册需要经历的繁琐的印刷过程&#xff0c;电子画册的制作大多…

RocketMQ教程(八):RocketMQ的集群搭建

集群架构 RocketMQ 的各个组件都可以搭建成集群部署,Broker 还可以搭建成主从架构,下面介绍的主要是 Broker 集群。 数据复制策略 复制策略是Broker的Master与Slave间的数据同步方式。分为同步复制与异步复制: 同步复制 消息写入master后,master会等待slave同步数据成…

【C++】——继承(详解)

一 继承的定义和概念 1.1 继承的定义 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保 持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类&#xff0c;被继承的称为基类…

wordpress主题开发

科普一&#xff1a;wordpress 是一套用 php 这个语言写的CMS后台管理系统&#xff0c;即我们大家的 wordpress 网站后台是一样的&#xff0c;能体现我们网站外观不同的地方就在于wordpress主题&#xff08;即皮肤&#xff09;&#xff0c;而这个主题的基本构成是 htmlcssjavasc…

使用Python爬取temu商品与评论信息

【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作&#xff01; 【&…

javaWeb项目-ssm+vue网上租车系统功能介绍

本项目源码&#xff1a;java-基于ssmvue的网上租车系统源码说明文档资料资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、…

系统运维联盟 5 月会议召开,围绕“进展、规划与合作”展开讨论

2024 年 5 月 28 日&#xff0c;龙蜥社区系统运维联盟&#xff08;SOMA&#xff0c;以下简称“运维联盟”&#xff09;月度会议于线上召开&#xff0c;12 家运维联盟单位、 20 位代表出席&#xff0c;缺席 1 家。本次会议由龙蜥社区运营委员会副主席、运维联盟秘书处负责人金美…

openh264 编码器源码分析:AnalyzePictureComplexity 函数

介绍 文件位置&#xff1a; openh264/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp 功能&#xff1a; 作为CWelsPreProcess类中一个方法&#xff0c;用来分析当前图像与参考图像之间的复杂度关系&#xff0c;以便编码策略。 原型&#xff1a; void CWels…

为什么要学习Flink系统管理及优化课程?

Flink系统是一种流式处理框架&#xff0c;能够高效地处理大规模数据流。然而&#xff0c;要确保Flink系统的正常运行&#xff0c;就需要进行系统管理和优化。系统管理是指对Flink集群的监控、调度和维护&#xff0c;而系统优化则是指通过调整参数和优化算法&#xff0c;提高Fli…

Class-Aware Self-Distillation for Remote SensingImage Scene Classification

这篇文章提出了一种新的蒸馏方式&#xff0c;由于遥感场景图像具有类间相似性和类内多样性的特点&#xff0c;这篇文章试图解决这个问题。通过三个共享权重的分支&#xff0c;同时输入三张图片&#xff0c;其中两张类别相同的图片&#xff0c;一张类别不同但地物特征相似的图片…

Go变量作用域精讲及代码实战

1. 变量的作用域概述 在编程中&#xff0c;变量的作用域&#xff08;Scope&#xff09;定义了变量在程序中的可见性和生命周期。理解变量的作用域对于编写健壮且可维护的代码至关重要。Go语言&#xff08;简称Go&#xff09;提供了几种不同的作用域类型&#xff0c;使得开发者可…

腾讯云SSL证书获取及Nginx配置教程

前言 很多人应该都有属于自己网站,刚开始基本是只能用http进行访问,无法使用https安全访问,但是随着网络安全意识的不断提高,越来越多的网站开始使用HTTPS协议来保护用户的数据安全,SSL证书是实现HTTPS协议的关键组件,本文将讲解如何在腾讯云上获取SSL证书,并配置到Ngi…

为什么要做数字化转型?数字化转型对企业的意义?

本人研究企业数字化转型9年多&#xff0c;为企业软件选型、数字化提供咨询服务&#xff01;目前重点研究低代码数字化转型玩法&#xff0c;力争为各行各业探索出一条最具性价比的数字化方式。 【织信低代码】数字化系统一体化的定制开发工具。 什么是数字化转型&#xff0c;为什…

【教程】从0开始搭建大语言模型:实现Attention机制

从0开始搭建大语言模型 从0开始搭建大语言模型&#xff1a;实现Attention机制建模长序列存在的问题使用attention机制获得数据间的依赖Self-attention介绍带有可训练权重的self-attention1.生成Q&#xff0c;K&#xff0c;V变量2.计算attention score3.attention weight的获得4…