当前位置: 首页 > article >正文

【Linux】线程池(第十八篇)

目录

线程池的概念

线程池的基本组成

实现方式

1. 定义任务

2. 创建线程池

3. 初始化和销毁线程池

4. 添加任务

注意事项


线程池的概念

线程池(Thread Pool)是一种基于池化技术设计用于管理线程的资源池。它预先创建并维护多个线程,这些线程等待执行新的任务。当有新任务到来时,线程池会分配一个空闲的线程来执行该任务,而不是为每个任务都创建一个新的线程。这样做的好处包括减少线程的创建和销毁的开销(线程创建和销毁是昂贵的操作),提高资源利用率,以及更好地控制并发执行的线程数量,避免过多的线程导致系统资源耗尽。

线程池的基本概念包括:

  1. 核心线程数(Core Threads):线程池中始终保持活跃的线程数量。即使这些线程是空闲的,它们也不会被销毁,而是等待新的任务到来。

  2. 最大线程数(Maximum Threads):线程池中允许的最大线程数量。当任务队列满了,且已运行的线程数小于最大线程数时,线程池会创建新的线程来执行任务。

  3. 任务队列(Work Queue):用于存放待执行的任务。当所有核心线程都在忙时,新来的任务会被添加到任务队列中等待。队列的实现可以是阻塞队列,也可以是其他类型的队列。

  4. 线程工厂(Thread Factory):用于创建新线程的工厂,允许自定义线程的创建过程,比如设置线程的名称、优先级、守护状态等。

  5. 拒绝策略(Rejected Execution Handler):当任务队列已满,且线程池中的线程数量已达到最大线程数时,新来的任务无法被立即执行。此时,需要有一种策略来处理这些任务,比如直接抛出异常、放弃任务、尝试将任务放入一个等待队列中(这个队列与任务队列不同,它用于存放那些因为线程池容量限制而被拒绝的任务),或者由调用者所在的线程来执行这个任务。

  6. 生命周期管理:线程池需要能够管理自身的生命周期,包括启动、运行和关闭。在关闭过程中,线程池需要等待所有已提交的任务完成(或者等待一定的时间后强制终止未完成的任务),并释放所有占用的资源。

线程池的基本组成

一个基本的线程池通常包含以下几个部分:

  1. 线程池管理器:管理线程池,包括创建和销毁线程、任务分配等。
  2. 工作线程:线程池中真正执行任务的线程。
  3. 任务队列:用于存放待处理的任务。
  4. 任务接口:每个任务需要实现的接口,以便工作线程可以调用执行任务。

实现方式

在 Linux 下,有多种方式可以实现线程池,比如使用 POSIX 线程(pthread)库。下面提供一个简化的基于 pthread 的线程池实现思路:

1. 定义任务

首先,定义一个任务结构体和任务执行函数。

typedef struct task { 
void (*func)(void *arg); 
void *arg; 
struct task *next; 
} task_t; 


void task_execute(void *arg) { 
task_t *task = (task_t *)arg; 
task->func(task->arg); 
free(task); 
}

2. 创建线程池

实现一个线程池管理器,管理线程和任务队列。


	#include <pthread.h> 

	#include <stdlib.h> 

	


	typedef struct { 

	pthread_mutex_t lock; 

	pthread_cond_t cond; 

	pthread_t *threads; 

	int num_threads; 

	task_t *head; 

	task_t *tail; 

	int shutdown; 

	} threadpool_t; 

	


	void *thread_function(void *arg) { 

	threadpool_t *pool = (threadpool_t *)arg; 

	


	while (1) { 

	pthread_mutex_lock(&pool->lock); 

	


	// 等待任务 

	while (pool->head == NULL && !pool->shutdown) { 

	pthread_cond_wait(&pool->cond, &pool->lock); 

	} 

	


	// 检查是否停止 

	if (pool->shutdown && pool->head == NULL) { 

	pthread_mutex_unlock(&pool->lock); 

	break; 

	} 

	


	// 取出任务 

	task_t *task = pool->head; 

	pool->head = task->next; 

	if (pool->head == NULL) { 

	pool->tail = NULL; 

	} 

	


	pthread_mutex_unlock(&pool->lock); 

	


	// 执行任务 

	if (task != NULL) { 

	task_execute(task); 

	} 

	} 

	


	return NULL; 

	} 

	


	// 初始化线程池... 

	// 添加任务到线程池... 

	// 销毁线程池...
3. 初始化和销毁线程池

初始化线程池时,你需要创建多个线程,并初始化互斥锁和条件变量。销毁时,需要设置关闭标志,并唤醒所有等待的线程,等待它们退出。

4. 添加任务

添加任务到线程池时,需要加锁保护任务队列,然后将任务添加到队列末尾,并唤醒一个等待的线程(如果有的话)。

注意事项

  • 同步和互斥:线程池中的任务添加、任务执行等操作需要同步和互斥机制来确保数据的一致性和线程安全。
  • 任务队列:任务队列可以基于链表、队列或其他数据结构实现,具体取决于任务类型和执行方式。
  • 错误处理:在实际应用中,需要考虑各种错误情况,如线程创建失败、任务执行出错等。

通过以上步骤,你可以实现一个基本的线程池来管理和执行并发任务。当然,实际应用中可能还需要考虑更多的细节和性能优化。


http://www.kler.cn/news/313193.html

相关文章:

  • 云计算第四阶段------CLOUD Day4---Day6
  • SpringBoot实现OAuth客户端
  • SQL编程题复习(24/9/20)
  • FPGA基本结构和简单原理
  • Mac下nvm无法安装node问题
  • 设计模式-行为型模式-命令模式
  • 001.从0开始实现线性回归(pytorch)
  • 【Docker】安装及使用
  • EmguCV学习笔记 C# 12.3 OCR
  • Vue vs React vs Angular 的区别和选择
  • 数据结构-2.9.双链表
  • 周末愉快!——周复盘
  • 深度学习-03 Pytorch
  • Android 空气质量刻度
  • CleanClip For Mac 強大的剪貼簿助手Paste替代工具 v2.2.1
  • 学习笔记——EffcientNetV2
  • React——点击事件函数调用问题
  • Gradio离线部署到内网,资源加载失败问题(Gradio离线部署问题解决方法)
  • docker搭建个人网盘,支持多种格式,还能画图,一键部署
  • Matlab可视化│常用绘图全家桶
  • HTTP中的301、302实现重定向
  • ActivityManagerService 分发广播(6)
  • Vue3:reactive丢失响应式,数据有更新但表单没有更新
  • gin配置swagger文档
  • 树与图的深度优先遍历(dfs的图论中的应用)
  • 【CPP】类与继承
  • [原创]全新安装最新版Delphi 12.2之前, 如何正确卸载旧版Delphi 12.1?
  • 谈对象第二弹: C++类和对象(中)
  • SQLiteHelper
  • Java:List<String> 转换List<BigDecimal> 并求和