线程池666666
1. 作用
线程池内部维护了多个工作线程,每个工作线程都会去任务队列中拿取任务并执行,当执行完一个任务后不是马上销毁,而是继续保留执行其它任务。显然,线程池提高了多线程的复用率,减少了创建和销毁线程的时间。
2. 实现原理
线程池内部由任务队列、工作线程和管理者线程组成。
任务队列:存储需要处理的任务。每个任务其实就是具体的函数,在任务队列中存储函数指针和对应的实参。当工作线程获取任务后,就能根据函数指针来调用指定的函数。其实现可以是数组、链表、STL容器等。
工作线程:有N个工作线程,每个工作线程会去任务队列中拿取任务,然后执行具体的任务。当任务被处理后,任务队列中就不再有该任务了。当任务队列中没有任务时,工作线程就会阻塞。
管理者线程:周期性检测忙碌的工作线程数量和任务数量。当任务较多线程不够用时,管理者线程就会多创建几个工作线程来加快处理(不会超过工作线程数量的上限)。当任务较少线程空闲多时,管理者线程就会销毁几个工作线程来减少内存占用(不会低于工作线程数量的下限)。
注意:线程池中没有维护“生产者线程”,所谓的“生产者线程”就是往任务队列中添加任务的线程。
3. 手撕线程池
参考来源:爱编程的大丙。
【1】threadpool.c:
#include "threadpool.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>#define NUMBER 2 //管理者线程增加或减少的工作线程数量//任务结构体
typedef struct Task {void (*func)(void* arg);void* arg;
} Task;//线程池结构体
struct ThreadPool {//任务队列,视为环形队列Task* taskQ;int queueCapacity; //队列容量int queueSize; //当前任务个数int queueFront; //队头 -> 取任务int queueRear; //队尾 -> 加任务//线程相关pthread_t managerID; //管理者线程IDpthread_t* threadIDs; //工作线程IDint minNum; //工作线程最小数量int maxNum; //工作线程最大数量int busyNum; //工作线程忙的数量int liveNum; //工作线程存活数量int exitNum; //要销毁的工作线程数量pthread_mutex_t mutexPool; //锁整个线程池pthread_mutex_t mutexBusy; //锁busyNumpthread_cond_t notFull; //任务队列是否满pthread_cond_t notEmpty; //任务队列是否空//线程池是否销毁int shutdown; //释放为1,否则为0
};/**************************************************************** 函 数: threadPoolCreate* 功 能: 创建线程池并初始化* 参 数: min---工作线程的最小数量* max---工作线程的最大数量* capacity---任务队列的最大容量* 返回值: 创建的线程池的地址**************************************************************/
ThreadPool* threadPoolCreate(int min, int max, int capacity)
{//申请线程池空间ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));do {//此处循环只是为了便于失败释放空间,只会执行一次if (pool == NULL) {printf("pool create error!\n");break;}//申请任务队列空间,并初始化pool->taskQ = (Task*)malloc(sizeof(Task) * capacity);if (pool->taskQ == NULL) {printf("Task create error!\n");break;}pool->queueCapacity = capacity;pool->queueSize = 0;pool->queueFront = 0;pool->queueRear = 0;//初始化互斥锁和条件变量if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 ||pthread_mutex_init(&pool->mutexBusy, NULL) != 0 ||pthread_cond_init(&pool->notFull, NULL) != 0 ||pthread_cond_init(&pool->notEmpty, NULL) != 0){printf("mutex or cond create error!\n");break;}//初始化shutdownpool->shutdown = 0;//初始化线程相关参数pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t) * max);if (pool->threadIDs == NULL) {printf("threadIDs create error!\n");break;}memset(pool->threadIDs, 0, sizeof(pthread_t) * max);pool->minNum = min;pool->maxNum = max;pool->busyNum = 0;pool->liveNum = min;pool->exitNum = 0;//创建管理者线程和工作线程pthread_create(&pool->managerID, NULL, manager, pool);//创建管理线程for (int i = 0; i < min; ++i) {pthread_create(&pool->threadIDs[i], NULL, worker, pool);//创建工作线程}return pool;} while (0);//申请资源失败,释放已分配的资源if (pool && pool->taskQ) free(pool->taskQ);if (pool && pool->threadIDs) free(pool->threadIDs);if (pool) free(pool);return NULL;
}/**************************************************************** 函 数: threadPoolDestroy* 功 能: 销毁线程池* 参 数: pool---要销毁的线程池* 返回值: 0表示销毁成功,-1表示销毁失败**************************************************************/
int threadPoolDestroy(ThreadPool* pool)
{if (!pool) return -1;//关闭线程池pool->shutdown = 1;//阻塞回收管理者线程pthread_join(pool->managerID, NULL);//唤醒所有工作线程,让其自杀for (int i = 0; i < pool->liveNum; ++i) {pthread_cond_signal(&pool->notEmpty);}//释放所有互斥锁和条件变量pthread_mutex_destroy(&pool->mutexBusy);pthread_mutex_destroy(&pool->mutexPool);pthread_cond_destroy(&pool->notEmpty);pthread_cond_destroy(&pool->notFull);//释放堆空间if (pool->taskQ) {free(pool->taskQ);pool->taskQ = NULL;}if (pool->threadIDs) {free(pool->threadIDs);pool->threadIDs = NULL;}free(pool);pool = NULL;return 0;
}/**************************************************************** 函 数: threadPoolAdd* 功 能: 生产者往线程池的任务队列中添加任务* 参 数: pool---线程池* func---函数指针,要执行的任务地址* arg---func指向的函数的实参* 返回值: 无**************************************************************/
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg)
{pthread_mutex_lock(&pool->mutexPool);//任务队列满,阻塞生产者while (pool->queueSize == pool->queueCapacity && !pool->shutdown) {pthread_cond_wait(&pool->notFull, &pool->mutexPool);}//判断线程池是否关闭if (pool->shutdown) {pthread_mutex_unlock(&pool->mutexPool);return;}//添加任务进pool->taskQpool->taskQ[pool->queueRear].func = func;pool->taskQ[pool->queueRear].arg = arg;pool->queueSize++;pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;pthread_cond_signal(&pool->notEmpty);//唤醒工作线程pthread_mutex_unlock(&pool->mutexPool);
}/**************************************************************** 函 数: getThreadPoolBusyNum* 功 能: 获取线程池忙的工作线程数量* 参 数: pool---线程池* 返回值: 忙的工作线程数量**************************************************************/
int getThreadPoolBusyNum(ThreadPool* pool)
{pthread_mutex_lock(&pool->mutexBusy);int busyNum = pool->busyNum;pthread_mutex_unlock(&pool->mutexBusy);return busyNum;
}/**************************************************************** 函 数: getThreadPoolAliveNum* 功 能: 获取线程池存活的工作线程数量* 参 数: pool---线程池* 返回值: 存活的工作线程数量**************************************************************/
int getThreadPoolAliveNum(ThreadPool* pool)
{pthread_mutex_lock(&pool->mutexPool);int liveNum = pool->liveNum;pthread_mutex_unlock(&pool->mutexPool);return liveNum;
}/**************************************************************** 函 数: worker* 功 能: 工作线程的执行函数* 参 数: arg---实参传入,这里传入的是线程池* 返回值: 空指针**************************************************************/
void* worker(void* arg)
{ThreadPool* pool = (ThreadPool*)arg;while (1) {/* 1.取出任务队列中的队头任务 */pthread_mutex_lock(&pool->mutexPool);//无任务就阻塞线程while (pool->queueSize == 0 && !pool->shutdown) {pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);//唤醒后,判断是不是要销毁线程if (pool->exitNum > 0) {//线程自杀pool->exitNum--;//销毁指标-1if (pool->liveNum > pool->minNum) {pool->liveNum--;//活着的工作线程-1pthread_mutex_unlock(&pool->mutexPool);threadExit(pool);}}}//线程池关闭了就退出线程if (pool->shutdown) {pthread_mutex_unlock(&pool->mutexPool);threadExit(pool);}//取出pool中taskQ的任务Task task;task.func = pool->taskQ[pool->queueFront].func;task.arg = pool->taskQ[pool->queueFront].arg;pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;//移动队头pool->queueSize--;//通知生产者添加任务pthread_cond_signal(&pool->notFull);pthread_mutex_unlock(&pool->mutexPool);/* 2.设置pool的busyNum+1 */pthread_mutex_lock(&pool->mutexBusy);pool->busyNum++;pthread_mutex_unlock(&pool->mutexBusy);/* 3.执行取出的任务 */printf("thread %ld start working ...\n", pthread_self());task.func(task.arg);free(task.arg);task.arg = NULL;printf("thread %ld end working ...\n", pthread_self());/* 4.设置pool的busyNum-1 */pthread_mutex_lock(&pool->mutexBusy);pool->busyNum--;pthread_mutex_unlock(&pool->mutexBusy);}return NULL;
}/**************************************************************** 函 数: manager* 功 能: 管理者线程的执行函数* 参 数: arg---实参传入,这里传入的是线程池* 返回值: 空指针**************************************************************/
void* manager(void* arg)
{ThreadPool* pool = (ThreadPool*)arg;while (!pool->shutdown) {/* 每隔3秒检测一次 */sleep(3);/* 获取pool中相关变量 */pthread_mutex_lock(&pool->mutexPool);int taskNum = pool->queueSize; //任务队列中的任务数量int liveNum = pool->liveNum; //存活的工作线程数量int busyNum = pool->busyNum; //忙碌的工作线程数量pthread_mutex_unlock(&pool->mutexPool);/* 功能一:增加工作线程,每次增加NUMBER个 *///当任务个数大于存活工作线程数,且存活工作线程数小于最大值if (taskNum > liveNum && liveNum < pool->maxNum) {pthread_mutex_lock(&pool->mutexPool);int counter = 0;for (int i = 0; i < pool->maxNum && counter < NUMBER&& pool->liveNum < pool->maxNum; ++i){if (pool->threadIDs[i] == 0) {pthread_create(&pool->threadIDs[i], NULL, worker, pool);counter++;pool->liveNum++;}}pthread_mutex_unlock(&pool->mutexPool);}/* 功能二:销毁工作线程,每次销毁NUMBER个 *///当忙的线程数*2 < 存活线程数,且存活线程数 > 最小线程数if (busyNum * 2 < liveNum && liveNum > pool->minNum) {pthread_mutex_lock(&pool->mutexPool);pool->exitNum = NUMBER;//唤醒NUMBER个工作线程,让其解除阻塞,在worker函数中自杀for (int i = 0; i < NUMBER; ++i) {pthread_cond_signal(&pool->notEmpty);}pthread_mutex_unlock(&pool->mutexPool);}}return NULL;
}/**************************************************************** 函 数: threadExit* 功 能: 工作线程退出函数,将工作线程的ID置为0,然后退出* 参 数: pool---线程池* 返回值: 无**************************************************************/
void threadExit(ThreadPool* pool)
{//将pool->threadIDs中的ID改为0pthread_t tid = pthread_self();for (int i = 0; i < pool->maxNum; i++) {if (pool->threadIDs[i] == tid) {pool->threadIDs[i] = 0;printf("threadExit() called, %ld exiting...\n", tid);break;}}pthread_exit(NULL);//退出
}
【2】threadpool.h:
#ifndef _THREADPOOL_H
#define _THREADPOOL_Htypedef struct ThreadPool ThreadPool;//创建线程池并初始化
ThreadPool* threadPoolCreate(int min, int max, int capacity);//销毁线程池
int threadPoolDestroy(ThreadPool* pool);//给线程池添加任务
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg);//获取当前忙碌的工作线程的数量
int getThreadPoolBusyNum(ThreadPool* pool);//获取当前存活的工作线程的数量
int getThreadPoolAliveNum(ThreadPool* pool);/*********************其它函数**********************/
void* worker(void* arg);//工作线程的执行函数
void* manager(void* arg);//管理者线程的执行函数
void threadExit(ThreadPool* pool);//线程退出函数#endif
【3】main.c:
#include <stdio.h>
#include "threadpool.h"
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>//任务函数,所有线程都执行此任务
void testFunc(void* arg)
{int* num = (int*)arg;printf("thread %ld is working, number = %d\n", pthread_self(), *num);sleep(1);
}int main()
{//创建线程池: 最少3个工作线程,最多10个,任务队列容量为100ThreadPool* pool = threadPoolCreate(3, 10, 100);//加入100个任务于任务队列for (int i = 0; i < 100; ++i) {int* num = (int*)malloc(sizeof(int));*num = i + 100;threadPoolAdd(pool, testFunc, num);}//销毁线程池sleep(30);//保证任务全部运行完毕threadPoolDestroy(pool);return 0;
}
【4】运行结果:
......
相关文章:

线程池666666
1. 作用 线程池内部维护了多个工作线程,每个工作线程都会去任务队列中拿取任务并执行,当执行完一个任务后不是马上销毁,而是继续保留执行其它任务。显然,线程池提高了多线程的复用率,减少了创建和销毁线程的时间。 2…...

Python28-5 k-means算法
k-means 算法介绍 k-means 算法是一种经典的聚类算法,其目的是将数据集分成 ( k ) 个不同的簇,每个簇内的数据点尽可能接近。算法的基本思想是通过反复迭代优化簇中心的位置,使得每个簇内的点与簇中心的距离之和最小。k-means 算法的具体步骤…...

主流国产服务器操作系统技术分析
主流国产服务器操作系统 信创 "信创",即信息技术应用创新,作为科技自立自强的核心词汇,在我国信息化建设的进程中扮演着至关重要的角色。自2016年起步,2020年开始蓬勃兴起,信创的浪潮正席卷整个信息与通信技…...

【Linux】线程封装与互斥(万字)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 文章目录 前言 C多线程的用法 对原生线程进行一次封装 理解pthread线程 Linux线程互斥 进程线程间的互斥相关背景概念 互斥量mutex 操作共享变量会有问题的售票…...

5分钟教你部署MySQL8.0环境
此方法基于Windows操作系统! 一、在MySQL官网单击downloads(下载)MySQLhttps://www.mysql.com/cn/ 选择在Windows操作系统下载 二、选择合适的版本 推荐下载第二种,安装时离线安装即可 三、安装MySQL8.0 1、找到MySQL下载完成…...

LLM应用:传统NLP任务
LLM出来以后,知乎上就出现了“传统NLP已死”的言论,但是传统NLP真的就被扔进历史的垃圾桶了吗? 其实,尽管LLM具有出色的通用能力,但仍然无法有效应对低资源领域的自然语言处理任务,如小语种翻译。为了更好地…...
基于Hadoop平台的电信客服数据的处理与分析③项目开发:搭建Kafka大数据运算环境---任务11:基础环境准备
任务描述 任务主要是安装配置基础环境,主要内容包括: 1、安装java Kafka和ZooKeeper都需要安装Java环境,推荐至少Java8及以上版本 2、安装ZooKeeper ZooKeeper是Kafka集群的必要组件 3、安装kafka Kafka版本包括使用的scala语言版本和kafka版…...
Golang中swtich中如何强制执行下一个代码块
switch 语句中的 case 代码块会默认带上 break,但可以使用 fallthrough 来强制执行下一个 case 代码块。 package mainimport ("fmt" )func main() {isSpace : func(char byte) bool {switch char {case : // 空格符会直接 break,返回 false…...
读书笔记-Java并发编程的艺术-第4章(Java并发编程基础)-第2节(启动和终止线程)
文章目录 4.2 启动和终止线程4.2.1 构造线程4.2.2 启动线程4.2.3 理解中断4.2.4 过期的suspend()、resume()和stop()4.2.5 安全地终止线程 4.2 启动和终止线程 在前面章节的示例中通过调用线程的start()方法进行启动,随着run()方法的执行完毕,线程也随之…...
通俗大白话理解Docker
什么是Docker Docker本质上是一种容器化技术,用于将应用程序及其所有依赖打包到一个标准化的单元中。这些单元(容器)可以在任何运行Docker的机器上运行。每个容器是相互隔离的,具有自己的文件系统、网络和进程空间。 以下是大白话…...
题解:CF1981C(Turtle and an Incomplete Sequence)
题解:CF1981C(Turtle and an Incomplete Sequence) Part 1:题意理解 地址链接:CF、洛谷。题面翻译:给定一个长度为 n n n 的序列 a a a,其中有一些元素未知,用 − 1 -1 −1 表示…...

Swift 中强大的 Key Paths(键路径)机制趣谈(上)
概览 小伙伴们可能不知道:在 Swift 语言中隐藏着大量看似“其貌不扬”实则却让秃头码农们“高世骇俗”,堪称卧虎藏龙的各种秘技。 其中,有一枚“不起眼”的小家伙称之为键路径(Key Paths)。如若将其善加利用ÿ…...

(十二)纹理和采样
纹理 在绘制三角形的过程中,将图片贴到三角形上进行显示的过程,就是纹理贴图的过程 uv坐标 如果如果图片尺寸和实际贴图尺寸不一致,就会导致像素不够用了的问题 纹理与采样 纹理对象(Texture):在GPU端,用来以一…...

QT创建地理信息shp文件编辑器shp_editor
空闲之余创建一个简单的矢量shp文件编辑器,加深对shp文件的理解。 一、启动程序 二、打开shp文件 三、显示shp文件的几何图形 四、双击右边表格中的feature,主窗体显示选中feature的各个节点。 五、鼠标在主窗体中选中feature的节点,按鼠标左…...

解析Kotlin中扩展函数与扩展属性【笔记摘要】
1.扩展函数 1.1 作用域:扩展函数写的位置不同,作用域就也不同 扩展函数可以写成顶层函数(Top-level Function),此时它只属于它所在的 package。这样你就能在任何类里使用它: package com.rengwuxianfun …...

【Java学习笔记】java图形界面编程
在前面的章节中,我们开发运行的应用程序都没有图形界面,但是很多应用软件,如Windows下的Office办公软件、扑克牌接龙游戏软件、企业进销存ERP系统等,都有很漂亮的图形界面。素以需要我们开发具有图形界面的软件。 Java图形界面编程…...

STM32入门笔记(03): ADC(SPL库函数版)(2)
A/D转换的常用技术有逐次逼近式、双积分式、并行式和跟踪比较式等。目前用的较多的是前3种。 A/D转换器的主要技术指标 转换时间 分辨率 例如,8位A/D转换器的数字输出量的变化范围为0~255,当输入电压的满刻度为5V时,数字量每变化…...

2024年7月2日 (周二) 叶子游戏新闻
老板键工具来唤去: 它可以为常用程序自定义快捷键,实现一键唤起、一键隐藏的 Windows 工具,并且支持窗口动态绑定快捷键(无需设置自动实现)。 卸载工具 HiBitUninstaller: Windows上的软件卸载工具 经典名作30周年新篇《恐怖惊魂夜…...
如何使用Spring Boot Profiles进行环境配置管理
如何使用Spring Boot Profiles进行环境配置管理 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨如何利用Spring Boot Profiles来管理不同环境…...
Java错题归纳(二)
1、若有如下接口A的定义,下列哪些类下确实现了该接口:C interface A { void method1(int i); void method2(int j); } A class B implements A{ void method1( ) { } void method2( ) { } } B class B implements A { void method1(int i ) { }…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...