线程池以及在QT中的接口使用

news2025/1/31 15:01:03

文章目录

  • 前言
      • 线程池架构组成
        • **一、任务队列(Task Queue)**
        • **二、工作线程组(Worker Threads)**
        • **三、管理者线程(Manager Thread)**
      • 系统协作流程图解
  • 一、QRunnable
  • 二、QThreadPool
  • 三、线程池的应用场景
      • Web服务器
      • 开发中的常见场景
      • 并行数据处理
      • 延时异步任务


前言

在并发编程中,当我们使用线程时,通常的做法是在需要时创建一个新的线程。这种方法实现起来较为简便,但存在一个显著问题:如果并发线程数量较多,且每个线程仅执行一个耗时较短的任务,频繁地创建和销毁线程将显著降低系统效率,因为线程的创建和销毁本身需要消耗一定的时间。

那么,是否存在一种机制可以使线程在执行完一个任务后不被销毁,而是能够继续执行其他任务,从而实现线程的复用呢?

线程池(Thread Pool)正是这样一种多线程处理模式。在线程池中,任务被添加到队列中,线程池在创建线程后会自动启动这些任务。线程池中的线程均为后台线程,每个线程使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中处于空闲状态(例如等待某个事件),线程池会插入另一个辅助线程以确保所有处理器保持忙碌状态。如果所有线程池线程都处于忙碌状态,但队列中仍有待处理的任务,线程池会在一段时间后创建另一个辅助线程,但线程总数不会超过预设的最大值。超过最大值的线程将被排队,等待其他线程完成任务后再启动。

线程池的概念在多种编程语言中均有体现,且许多语言直接提供了线程池的实现,开发者可以直接使用。接下来,我们将详细介绍线程池的实现原理。

线程池架构组成

线程池系统由三大核心组件协同构成,各组件功能职责如下:

一、任务队列(Task Queue)
  1. 功能定义
    • 作为任务缓冲区,负责存储待处理的任务集合
    • 采用先进先出(FIFO)调度策略,确保任务有序执行
  2. 操作接口
    • 通过线程池API提供任务添加(enqueue)与删除(dequeue)操作
    • 已处理任务由系统自动移出队列
  3. 生产者角色
    • 调用线程池API向队列提交任务的线程称为生产者线程(Producer Thread)

二、工作线程组(Worker Threads)
  1. 线程配置
    • 维护固定数量(N)的常驻工作线程
    • 作为任务队列的消费者(Consumer),持续执行以下操作:
      • 从任务队列中提取待处理任务
      • 执行任务处理逻辑
  2. 阻塞机制
    • 当任务队列为空时,工作线程通过条件变量(Condition Variable)或信号量(Semaphore)进入阻塞状态
    • 生产者提交新任务后触发唤醒机制,恢复工作线程执行
  3. 资源隔离性
    • 工作线程仅关注任务执行,不参与线程池状态管理

三、管理者线程(Manager Thread)
  1. 监控职责
    • 独立于任务处理流程,周期性执行以下检测:
      • 任务队列的积压数量
      • 当前处于忙碌状态的工作线程数
  2. 动态调节策略
    • 扩容机制:当任务负载超过阈值时,动态创建新增工作线程
    • 缩容机制:当系统闲置率过高时,安全销毁冗余工作线程
  3. 设计目标
    • 实现资源利用效率与任务吞吐量的平衡优化

系统协作流程图解

在这里插入图片描述

通过三者的协同配合,线程池实现了任务分发、资源调度与负载均衡的闭环管理,有效提升多线程环境下的任务处理效能与系统稳定性。

一、QRunnable

QRunnable类 常用函数不多,主要是设置任务对象传给线程池后,是否需要自动析构。

// 在子类中必须要重写的函数, 里边是任务的处理流程
[pure virtual] void QRunnable::run();

// 参数设置为 true: 这个任务对象在线程池中的线程中处理完毕, 这个任务对象就会自动销毁
// 参数设置为 false: 这个任务对象在线程池中的线程中处理完毕, 对象需要程序猿手动销毁
void QRunnable::setAutoDelete(bool autoDelete);
// 获取当前任务对象的析构方式,返回true->自动析构, 返回false->手动析构
bool QRunnable::autoDelete() const;

创建一个要添加到线程池中的任务类,处理方式如下:

class MyWork : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit MyWork(QObject *parent = nullptr)
    {
        // 任务执行完毕,该对象自动销毁
        setAutoDelete(true);
    }
    ~MyWork();

    void run() override{}
}

在上面的示例中MyWork类是一个多重继承,如果需要在这个任务中使用Qt的信号槽机制进行数据的传递就必须继承QObject这个类,如果不使用信号槽传递数据就可以不继承了,只继承QRunnable即可。

class MyWork :public QRunnable
{
    Q_OBJECT
public:
    explicit MyWork()
    {
        // 任务执行完毕,该对象自动销毁
        setAutoDelete(true);
    }
    ~MyWork();

    void run() override{}
}

二、QThreadPool

Qt中的 QThreadPool 类管理了一组 QThreads, 里边还维护了一个任务队列。QThreadPool 管理和回收各个 QThread 对象,以帮助减少使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。也可以单独创建一个 QThreadPool 对象使用。


线程池常用的API函数如下:

// 获取和设置线程中的最大线程个数
int maxThreadCount() const;
void setMaxThreadCount(int maxThreadCount);

// 给线程池添加任务, 任务是一个 QRunnable 类型的对象
// 如果线程池中没有空闲的线程了, 任务会放到任务队列中, 等待线程处理
void QThreadPool::start(QRunnable * runnable, int priority = 0);
// 如果线程池中没有空闲的线程了, 直接返回值, 任务添加失败, 任务不会添加到任务队列中
bool QThreadPool::tryStart(QRunnable * runnable);

// 线程池中被激活的线程的个数(正在工作的线程个数)
int QThreadPool::activeThreadCount() const;

// 尝试性的将某一个任务从线程池的任务队列中删除, 如果任务已经开始执行就无法删除了
bool QThreadPool::tryTake(QRunnable *runnable);
// 将线程池中的任务队列里边没有开始处理的所有任务删除, 如果已经开始处理了就无法通过该函数删除了
void QThreadPool::clear();

// 在每个Qt应用程序中都有一个全局的线程池对象, 通过这个函数直接访问这个对象
static QThreadPool * QThreadPool::globalInstance();

一般情况下,我们不需要在Qt程序中创建线程池对象,直接使用Qt为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用start()方法就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护。

具体的使用方式如下:

mywork.h

class MyWork :public QRunnable
{
    Q_OBJECT
public:
    explicit MyWork();
    ~MyWork();

    void run() override;
}

mywork.cpp

MyWork::MyWork() : QRunnable()
{
    // 任务执行完毕,该对象自动销毁
    setAutoDelete(true);
}
void MyWork::run()
{
    // 业务处理代码
    ......
}

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 线程池初始化,设置最大线程池数
    QThreadPool::globalInstance()->setMaxThreadCount(4);
    // 添加任务
    MyWork* task = new MyWork;
    QThreadPool::globalInstance()->start(task);    
}

三、线程池的应用场景

Web服务器

在处理HTTP请求时,每个请求都可以作为一个独立的任务提交到线程池中,由线程池中的线程处理,这样做的好处是可以快速响应用户请求,同时复用线程资源

开发中的常见场景

异步任务处理
例如发送电子邮件、执行后台计算等这些都可以作为异步任务提交给线程池,从而不会阻塞主程序的执行。

并行数据处理

比如我从mysql中去取出10万条数据,结果集是一个List,分批次处理,每个批次1000条,在for循环中循环的次数就是批次的数目,每次循环会提交给线程池一个异步运算任务,比如这里会分为100个批次,那么会for循环100次提交100个异步运算任务,线程池中的线程会并行去处理这些批次的数据,然后再把每个处理后的批次组合为一个最终结果

延时异步任务

我需要开启一个异步任务,但这个异步任务需要等待30秒后再执行,最简单粗暴的方法是使用Thread.sleep方法,但这种的话会造成线程资源的浪费,高并发情况下就容易出现线程资源紧缺的问题。

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

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

相关文章

联想拯救者R720笔记本外接显示屏方法,显示屏是2K屏27英寸

晚上23点10分前下单,第二天上午显示屏送到,检查外包装没拆封过。这个屏幕左下方有几个按键,按一按就开屏幕、按一按就关闭屏幕,按一按方便节省时间,也支持阅读等模式。 显示屏是 :AOC 27英寸 2K高清 100Hz…

C++ deque(1)

1.deque介绍 deque的扩容不像vector那样麻烦 直接新开一个buffer 不用重新开空间再把数据全部移过去 deque本质上是一个指针数组和vector<vector>不一样&#xff0c;vector<vector>本质上是一个vector对象数组&#xff01;并且vector<vector>的buffer是不一…

Brightness Controller-源码记录

Brightness Controller 亮度控制 一、概述二、ddcutil 与 xrandr1. ddcutil2. xrandr 三、部分代码解析1. icons2. ui3. utilinit.py 一、概述 项目&#xff1a;https://github.com/SunStorm2018/Brightness.git 原理&#xff1a;Brightness Controlle 是我在 Ubuntu 发现上调…

Java8_StreamAPI

Stream 1.创建流 1.1 集合创建流 List<String> list List.of("a", "b", "c"); Stream<String> stream list.stream(); stream.forEach(System.out::println);1.2 数组创建流 String[] array {"a","b",&qu…

【架构面试】二、消息队列和MySQL和Redis

MQ MQ消息中间件 问题引出与MQ作用 常见面试问题&#xff1a;面试官常针对项目中使用MQ技术的候选人提问&#xff0c;如如何确保消息不丢失&#xff0c;该问题可考察候选人技术能力。MQ应用场景及作用&#xff1a;以京东系统下单扣减京豆为例&#xff0c;MQ用于交易服和京豆服…

OpenEuler学习笔记(十六):搭建postgresql高可用数据库环境

以下是在OpenEuler系统上搭建PostgreSQL高可用数据环境的一般步骤&#xff0c;通常可以使用流复制&#xff08;Streaming Replication&#xff09;或基于Patroni等工具来实现高可用&#xff0c;以下以流复制为例&#xff1a; 安装PostgreSQL 配置软件源&#xff1a;可以使用O…

论文阅读(十一):基因-表型关联贝叶斯网络模型的评分、搜索和评估

1.论文链接&#xff1a;Scoring, Searching and Evaluating Bayesian Network Models of Gene-phenotype Association 摘要&#xff1a; 全基因组关联研究&#xff08;GWAS&#xff09;的到来为识别常见疾病的遗传变异&#xff08;单核苷酸多态性&#xff08;SNP&#xff09;&…

企业微信远程一直显示正在加载

企业微信远程一直显示正在加载 1.问题描述2.问题解决 系统&#xff1a;Win10 1.问题描述 某天使用企业微信给同事进行远程协助的时候&#xff0c;发现一直卡在正在加载的页面&#xff0c;如下图所示 2.问题解决 经过一番查找资料后&#xff0c;我发现可能是2个地方出了问题…

人工智能 - 1

深度强化学习&#xff08;Deep Reinforcement Learning&#xff09; 图神经网络&#xff08;Graph Neural Networks, GNNs&#xff09; Transformer 一种深度学习模型 大语言模型&#xff08;Large Language Models, LLMs&#xff09; 人工智能 • Marvin Minsky 将其定义…

Linux_线程同步生产者消费者模型

同步的相关概念 同步&#xff1a;在保证数据安全的前提下&#xff0c;让线程能够按照某种特定的顺序访问临界资源&#xff0c;从而有效避免饥饿问题&#xff0c;叫做同步竞态条件&#xff1a;因为时序问题&#xff0c;而导致程序异常&#xff0c;我们称之为竞态条件。 同步的…

Github 2025-01-30 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2025-01-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:2724 次关注人…

【Rust自学】17.2. 使用trait对象来存储不同值的类型

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 17.2.1. 需求 这篇文章以一个例子来介绍如何在Rust中使用trait对象来存储不同值的类型。 …

VLLM性能调优

1. 抢占 显存不够的时候&#xff0c;某些request会被抢占。其KV cache被清除&#xff0c;腾退给其他request&#xff0c;下次调度到它&#xff0c;重新计算KV cache。 报这条消息&#xff0c;说明已被抢占&#xff1a; WARNING 05-09 00:49:33 scheduler.py:1057 Sequence gr…

Java线程认识和Object的一些方法

本文目标&#xff1a; 要对Java线程有整体了解&#xff0c;深入认识到里面的一些方法和Object对象方法的区别。认识到Java对象的ObjectMonitor&#xff0c;这有助于后面的Synchronized和锁的认识。利用Synchronized wait/notify 完成一道经典的多线程题目&#xff1a;实现ABC…

数据库管理-第287期 Oracle DB 23.7新特性一览(20250124)

数据库管理287期 2025-01-24 数据库管理-第287期 Oracle DB 23.7新特性一览&#xff08;20250124&#xff09;1 AI向量搜索&#xff1a;算术和聚合运算2 更改Compatible至23.6.0&#xff0c;以使用23.6或更高版本中的新AI向量搜索功能3 Cloud Developer包4 DBMS_DEVELOPER.GET_…

【MySQL】MySQL客户端连接用 localhost和127.0.0.1的区别

# systemctl status mysqld # ss -tan | grep 3306 # mysql -V localhost与127.0.0.1的区别是什么&#xff1f; 相信有人会说是本地IP&#xff0c;曾有人说&#xff0c;用127.0.0.1比localhost好&#xff0c;可以减少一次解析。 看来这个入门问题还有人不清楚&#xff0c;其实…

MySQL(高级特性篇) 14 章——MySQL事务日志

事务有4种特性&#xff1a;原子性、一致性、隔离性和持久性 事务的隔离性由锁机制实现事务的原子性、一致性和持久性由事务的redo日志和undo日志来保证&#xff08;1&#xff09;REDO LOG称为重做日志&#xff0c;用来保证事务的持久性&#xff08;2&#xff09;UNDO LOG称为回…

【Block总结】HWD,小波下采样,适用分类、分割、目标检测等任务|即插即用

论文信息 Haar wavelet downsampling (HWD) 是一项针对语义分割的创新模块&#xff0c;旨在通过减少特征图的空间分辨率来提高深度卷积神经网络&#xff08;DCNNs&#xff09;的性能。该论文的主要贡献在于提出了一种新的下采样方法&#xff0c;能够在下采样阶段有效地减少信息…

【解决方案】MuMu模拟器移植系统进度条卡住98%无法打开

之前在Vmware虚拟机里配置了mumu模拟器&#xff0c;现在想要移植到宿主机中 1、虚拟机中的MuMu模拟器12-1是目标系统&#xff0c;对应的目录如下 C:\Program Files\Netease\MuMu Player 12\vms\MuMuPlayer-12.0-1 2、Vmware-虚拟机-设置-选项&#xff0c;启用共享文件夹 3、复…

力扣面试150 快乐数 循环链表找环 链表抽象 哈希

Problem: 202. 快乐数 &#x1f469;‍&#x1f3eb; 参考题解 Code public class Solution {public int squareSum(int n) {int sum 0;while(n > 0){int digit n % 10;sum digit * digit;n / 10;}return sum;}public boolean isHappy(int n) {int slow n, fast squa…