Linux手搓线程池!(C和C++版)

线程池本身只维护「消费者工作线程」,生产者不属于线程池内部成员,但完整业务模型一定同时存在生产者 & 消费者。

C版本

代码

cpp 复制代码
//threadpool.cpp
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include"threadpool.h"
#include<string.h>
#include<stdlib.h>
//任务队列
struct Task {
	void (*function)(void* arg);//定义函数指针,指向函数地址,void* 万能指针,通过强转可以仁义定arg类型义
	void* arg;
};
int num=2;//一次增加/减少线程个数
//线程池结构体
struct ThreadPool {
	Task* task;//任务队列(环形),指向Task结构体指针,`task` 这个变量保存一块内存的起始地址,可理解为数组
	int QueueFront;//队头坐标,取任务
	int QueueBehind;//队尾坐边,放任务
	int QueueCapacity;//队列最大任务数
	int QueueSize;//任务队列现在有的任务数
	pthread_t* threadIDs;//工作线程ID,消费者线程
	pthread_t managerID;//管理者ID,只有一个
	int maxthread;//最多线程数
	int minthread;//最少线程数
	int livethread;//存活线程
	int busythread;//接受任务忙活线程
	int exitthread;//要销毁的线程数
	int shutdown;//线程池是否要被销毁,销毁1,不销毁0
	pthread_mutex_t mutex;      // 保护线程池临界资源(队列、liveNum、queueSize等)
	pthread_cond_t notfull;     // 条件变量:队列**不满**,生产者可以放任务
	pthread_cond_t notempty;    // 条件变量:队列**不为空**,worker可以取任务

};
void taskfunction(void* arg) {
	int number = *(int*) arg;
	printf("thread %ld is working, number = %d\n",
		pthread_self(), number);
	sleep(1);
	free(arg);
}
int main() {
	//创建线程池
	ThreadPool* pool=threadPoolCreate(10,20,3);
	for (int i = 0; i < 18; i++) {
		int* number = (int*)malloc(sizeof(int));
		*number = i;
		queueAdd(pool, taskfunction, number);
	}
	sleep(13);

	threadPoolDestroy(pool);
	return 0;
}
//创建线程池并初始化,max传入的最多线程数 QueueCapacity队列最大任务数 minthread最少线程数
ThreadPool* threadPoolCreate(int maxthread,int QueueCapacity,int minthread) {
	//分配内存!!
	ThreadPool* pool= (ThreadPool*)malloc(sizeof(ThreadPool));//创建线程池并开辟堆内存
	pool->task = (Task*)malloc(sizeof(Task) * QueueCapacity);//任务队列分配内存
	pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t) * maxthread);//给工作线程Id分配内存
	
	//初始化!!
	memset(pool->threadIDs, 0, sizeof(pthread_t) * maxthread);
	pool->QueueCapacity = QueueCapacity;
	pool->QueueSize = 0;
	pool->maxthread = maxthread;
	pool->minthread = minthread;
	pool->QueueFront = 0;
	pool->QueueBehind = 0;
	pool->busythread = 0;
	pool->livethread = minthread;
	pool->exitthread = 0;
	pool->shutdown = 0;
	if (pthread_mutex_init(&pool->mutex, NULL) != 0//=0创建成功
		|| pthread_cond_init(&pool->notempty, NULL) != 0
		|| pthread_cond_init(&pool->notfull, NULL) != 0) {
		printf("mutex or condition init failQAQ\n");
	}

	
	//创建线程
	pthread_create(&pool->managerID, NULL, manager, pool);//传入pool指针,让子线程能访问线程池
	for (int i = 0; i < minthread; i++) {
		pthread_create(&pool->threadIDs[i], NULL, worker, pool);
		printf("pthread create ID:%ld\n", pool->threadIDs[i]);
	}
	return pool;
	
}
//线程退出函数
void ThreadExit(ThreadPool* pool) {
	//获取当前线程Id
	pthread_t tid = pthread_self();
	//循环在线程id数组里面找到该线程id,把他置为0
	for (int i = 0; i < pool->maxthread; i++) {
		if (pool->threadIDs[i] == tid) {
			pool->threadIDs[i] = 0;
			break;
		}
	}
	//主动结束当前正在运行的这条线程
	printf("threadexit ID:%ld\n", tid);
	pthread_exit(NULL);
	
}
	
//线程池销毁函数
void threadPoolDestroy(ThreadPool* pool) {
	//此时才意识到为什么要加上shudown变量来判断线程池是否要被销毁
	//通过shutdowm就能判断代码其他函数线程运行时需不需要被销毁
	if (pool == NULL) {
		printf("threadpool is NULL\n");
		return;
	}
	pool->shutdown = 1;
	//等待manager线程自己运行结束,然后回收它的资源
	pthread_join(pool->managerID, NULL);
	//循环遍历每一个活着线程(线程退出了就不是活着了),让他们都退出
	for (int i = 0; i < pool->livethread; i++) {
		pthread_cond_broadcast(&pool->notempty);
	}
	//释放堆内存
	if (pool->task) {
		free(pool->task);
	}
	if (pool->threadIDs) {
		free(pool->threadIDs);
	}
	//销毁互斥锁条件变量
	pthread_mutex_destroy(&pool->mutex);
	pthread_cond_destroy(&pool->notempty);
	pthread_cond_destroy(&pool->notfull);
	//释放线程池
	free(pool);
	//pool置空防止产生野指针
	pool = NULL;
}
//管理者操作函数
void* manager(void* arg) {
	ThreadPool* pool = (ThreadPool*)arg;
	while (1) {
		//每隔三面检查一次
		sleep(3);
		//获取变量
		pthread_mutex_lock(&pool->mutex);
		//检查是否要关闭线程池
		if (pool->shutdown == 1) {
			pthread_mutex_unlock(&pool->mutex);
			printf("namagerclose managerID:%ld\n", pthread_self());
			return NULL;
		}

		int livethread = pool->livethread;
		int busythread = pool->busythread;
		int QueueSize = pool->QueueSize;
		int maxthread = pool->maxthread;
		int minthread = pool->minthread;
		//增加线程
		if (livethread < QueueSize && livethread < maxthread) {
			
			int count = 0;//用于记录增加的线程数
			for (int i = 0; i < maxthread&&count<num&& livethread < maxthread; i++) {
				if (pool->threadIDs[i] == 0) {
					pthread_create(&pool->threadIDs[i], NULL, worker, pool);
					printf("live:%d queuesize:%d addthread ID:%ld\n",pool->livethread,pool->QueueSize,pool->threadIDs[i]);
					count++;
					pool->livethread++;
				}
			}
		}

		//销毁线程
		if (busythread * 2 < livethread && livethread > minthread) {
			pool->exitthread += num;
			//让线程自杀
			for (int i = 0; i < num; i++) {
				pthread_cond_signal(&pool->notempty);//唤醒notempty条件变量
			}
		}
		pthread_mutex_unlock(&pool->mutex);
	}
}
//工作线程(消费者)操作函数
void* worker(void* arg) {
	ThreadPool* pool =(ThreadPool*)arg;
	while (1) {
		pthread_mutex_lock(&pool->mutex);
		//判断任务队列是否为空
		while (pool->QueueSize == 0&&pool->shutdown==0) {
			pthread_cond_wait(&pool->notempty, &pool->mutex);//阻塞等待不为空时唤醒
			//判断是否要销毁线程
			if (pool->exitthread > 0) {
				pool->exitthread--;
				pool->livethread--;
				printf("busy:%d,live:%d destroy estra thread:%ld\n",pool->busythread,pool->livethread,pthread_self());
				pthread_mutex_unlock(&pool->mutex);
				ThreadExit(pool);
				
			}
		}
		//是否要关闭线程池
		if (pool->shutdown == 1) {
			pthread_mutex_unlock(&pool->mutex);
			ThreadExit(pool);
		}
		//从队列中取任务
		Task task;
		task.function = pool->task[pool->QueueFront].function;
		task.arg = pool->task[pool->QueueFront].arg;
		//取完一个任务,头坐标要动态移动
		pool->QueueFront = (pool->QueueFront + 1) % pool->QueueCapacity;
		pool->QueueSize--;

		pool->busythread++;
		printf("threadID:%ld get task\n", pthread_self());
		//取完告诉生产者 不满 可以生产了
		pthread_cond_signal(&pool->notfull);
		pthread_mutex_unlock(&pool->mutex);

		//执行任务
		task.function(task.arg);
		pthread_mutex_lock(&pool->mutex);
		pool->busythread--;
		pthread_mutex_unlock(&pool->mutex);
	}
	return NULL;
}
//增加任务函数
void queueAdd(ThreadPool* pool, void (*function)(void*),void* arg) {
	pthread_mutex_lock(&pool->mutex);
	//判断任务队列是否满了
	while(pool->QueueSize == pool->QueueCapacity) {
		pthread_cond_wait(&pool->notfull,&pool->mutex);
	}
	//队列中添加任务
	pool->task[pool->QueueBehind].function = function;
	pool->task[pool->QueueBehind].arg = arg;
	pool->QueueBehind = (pool->QueueBehind + 1) % pool->QueueCapacity;
	pool->QueueSize++;
	printf("add task\n");
	//唤醒消费者
	pthread_cond_signal(&pool->notempty);
	pthread_mutex_unlock(&pool->mutex);
	
}
cpp 复制代码
//threadpool.h
#pragma once
#ifndef _THREADPOOL_H
#define _THREADPOOL_H
typedef struct ThreadPool ThreadPool;
//管理者操作函数
void* manager(void* arg);

//创建线程池并初始化
ThreadPool* threadPoolCreate(int maxthread,int QueueCapacity,int minthread);

//线程退出
void ThreadExit(ThreadPool* pool);

//增加任务函数
void queueAdd(ThreadPool* pool, void (*function)(void*), void* arg);

//工作线程(消费者)操作函数
void* worker(void* arg);

//线程池销毁函数
void threadPoolDestroy(ThreadPool* pool);

#endif  // _THREADPOOL_H

函数指针:

void (*function)(void* arg)函数指针变量 ,用来存放回调函数地址;void* function(void* arg)只是一个函数声明,表示该函数执行后返回万能指针,二者语法含义完全不同。

关于livenum和busynum

解释为什么manager中增加线程的方式是

任务的个数>存活的线程个数 && 存活的线程数<最大线程数

任务个数:任务队列还没有被领走的任务,代表存活着的线程数不够,我们当然想多增加线程把这些任务领走

销毁线程:

复制代码
忙的线程*2 < 存活的线程数 && 存活的线程>最小线程数

忙的线程是真正处理任务的,明显空闲下来线程太多了,占用资源,销毁!!!

不在manager里面销毁

相当于我们要在工作线程中去判断当前线程是不是空闲的,然后自杀

销毁线程池核心目标:

安全停止所有线程 → 回收线程资源 → 释放堆内存 → 销毁锁和条件变量,防止内存泄漏 流程顺序原则:先标记关闭信号 → 回收管理线程 → 唤醒所有工作线程退出 → 释放资源

打关闭标记 → 等管理线程退出 → 唤醒所有工作线程退出 → 回收全部线程资源 → 释放堆内存 → 销毁锁与条件变量

C++版本

代码

cpp 复制代码
//main.cpp
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include"Task.h"
#include "pthreadpool.h"

void taskfunction(void* arg) {
	int* p = static_cast<int*>(arg);
	int num = *p;
	printf("thread %ld working, num=%d\n", pthread_self(), num);
	sleep(1);
	delete p;   // 释放真正的int指针
}
int main() {
	//创建线程池
	ThreadPool* pool = new ThreadPool(10,3);
	for (int i = 0; i < 18; i++) {
		int* number = new int;
		*number = i;
		Task t;
		t.function = taskfunction;
		t.arg = number;

		// 传入完整Task对象
		pool->addTask(t);
	}
	sleep(13);

	delete pool;
	return 0;
}
cpp 复制代码
//pthreadpool.cpp
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include"pthreadpool.h"
#include<string.h>
//任务队列

int num = 2;//一次增加/减少线程个数
//创建线程池并初始化,max传入的最多线程数,minthread最少线程数
ThreadPool::ThreadPool(int maxthread, int minthread) {
	//实例化Task
	m_taskQueue = new TaskQueue;
	//给threadid开辟一段堆内存
	threadIDs = new pthread_t[maxthread];
	//初始化!!
	memset(threadIDs, 0, sizeof(pthread_t) * maxthread);
	this->maxthread = maxthread;
	this->minthread = minthread;
	busythread = 0;
	livethread = minthread;
	exitthread = 0;
	shutdown = 0;
	if (pthread_mutex_init(&mutex, NULL) != 0//=0创建成功
		|| pthread_cond_init(&notempty, NULL) != 0) {
		printf("mutex or condition init failQAQ\n");
	}
	//创建线程
	pthread_create(&managerID, NULL, manager, this);//传入pool指针,让子线程能访问线程池
	for (int i = 0; i < minthread; i++) {
		pthread_create(&threadIDs[i], NULL, worker, this);
		printf("pthread create ID:%ld\n", threadIDs[i]);
	}
}
//线程池销毁函数
ThreadPool::~ThreadPool() {
	//此时才意识到为什么要加上shudown变量来判断线程池是否要被销毁
	//通过shutdowm就能判断代码其他函数线程运行时需不需要被销毁
	shutdown = 1;
	//等待manager线程自己运行结束,然后回收它的资源
	pthread_join(managerID, NULL);
	//循环遍历每一个活着线程(线程退出了就不是活着了),让他们都退出
	for (int i = 0; i < livethread; i++) {
		pthread_cond_broadcast(&notempty);
	}
	//释放堆内存
	if (m_taskQueue) {
		delete m_taskQueue;
	}
	if (threadIDs) {
		delete threadIDs;
	}
	//销毁互斥锁条件变量
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&notempty);
}
//添加任务
void ThreadPool::addTask(Task& task) {
	if (shutdown) {
		return;
	}
	m_taskQueue->addTask(task);
	pthread_cond_signal(&notempty);
}
//线程退出函数
void ThreadPool::ThreadExit() {
	//获取当前线程Id
	pthread_t tid = pthread_self();
	//循环在线程id数组里面找到该线程id,把他置为0
	for (int i = 0; i < maxthread; i++) {
		if (threadIDs[i] == tid) {
			threadIDs[i] = 0;
			break;
		}
	}
	//主动结束当前正在运行的这条线程
	printf("threadexit ID:%ld\n", tid);
	pthread_exit(NULL);

}

//管理者操作函数
void* ThreadPool::manager(void* arg) {
	ThreadPool* pool = (ThreadPool*)arg;
	while (1) {
		//每隔三面检查一次
		sleep(3);
		//获取变量
		pthread_mutex_lock(&pool->mutex);
		//检查是否要关闭线程池
		if (pool->shutdown == 1) {
			pthread_mutex_unlock(&pool->mutex);
			printf("namagerclose managerID:%ld\n", pthread_self());
			return NULL;
		}

		int livethread = pool->livethread;
		int busythread = pool->busythread;
		int maxthread = pool->maxthread;
		int minthread = pool->minthread;
		//增加线程
		if (livethread < pool->m_taskQueue->getSize() && livethread < maxthread) {

			int count = 0;//用于记录增加的线程数
			for (int i = 0; i < maxthread && count < num && livethread < maxthread; i++) {
				if (pool->threadIDs[i] == 0) {
					pthread_create(&pool->threadIDs[i], NULL, worker, pool);
					printf("live:%d queuesize:%d addthread ID:%ld\n", pool->livethread, pool->m_taskQueue->getSize(), pool->threadIDs[i]);
					count++;
					pool->livethread++;
				}
			}
		}

		//销毁线程
		if (busythread * 2 < livethread && livethread > minthread) {
			pool->exitthread += num;
			//让线程自杀
			for (int i = 0; i < num; i++) {
				pthread_cond_signal(&pool->notempty);//唤醒notempty条件变量
			}
		}
		pthread_mutex_unlock(&pool->mutex);
	}
}
//工作线程(消费者)操作函数
void* ThreadPool::worker(void* arg) {
	ThreadPool* pool = (ThreadPool*)arg;
	while (1) {
		pthread_mutex_lock(&pool->mutex);
		//判断任务队列是否为空
		while (pool->m_taskQueue->getSize() == 0 && pool->shutdown == 0) {
			pthread_cond_wait(&pool->notempty, &pool->mutex);//阻塞等待不为空时唤醒
			//判断是否要销毁线程
			if (pool->exitthread > 0) {
				pool->exitthread--;
				pool->livethread--;
				printf("busy:%d,live:%d destroy estra thread:%ld\n", pool->busythread, pool->livethread, pthread_self());
				pthread_mutex_unlock(&pool->mutex);
				pool->ThreadExit();

			}
		}
		//是否要关闭线程池
		if (pool->shutdown == 1) {
			pthread_mutex_unlock(&pool->mutex);
			pool->ThreadExit();
		}
		//从队列中取任务
		Task task;
		task = pool->m_taskQueue->takeTask();
		pool->busythread++;
		printf("threadID:%ld get task\n", pthread_self());
		pthread_mutex_unlock(&pool->mutex);

		//执行任务
		task.function(task.arg);
		pthread_mutex_lock(&pool->mutex);
		pool->busythread--;
		pthread_mutex_unlock(&pool->mutex);
		//delete task.arg;
	}
	return nullptr;
}
cpp 复制代码
//pthreadpool.h
#pragma once
#include "Task.h"

//线程池类
class ThreadPool {
public:
	//线程池初始化
	ThreadPool(int maxthread,int minthread);
	//线程池销毁
	~ThreadPool();
	//添加任务
	void addTask(Task& task);
	
private:
	//工作线程
	static void* worker(void* arg);
	//管理者线程
	static void* manager(void* arg);
	//线程退出函数
	void ThreadExit();
	TaskQueue* m_taskQueue;
	pthread_t* threadIDs;//工作线程ID,消费者线程
	pthread_t managerID;//管理者ID,只有一个
	int maxthread;//最多线程数
	int minthread;//最少线程数
	int livethread;//存活线程
	int busythread;//接受任务忙活线程
	int exitthread;//要销毁的线程数
	int shutdown;//线程池是否要被销毁,销毁1,不销毁0
	pthread_mutex_t mutex;      // 保护线程池临界资源(队列、liveNum、queueSize等)
	pthread_cond_t notempty;    // 条件变量:队列**不为空**,worker可以取任务
};
cpp 复制代码
//Task.cpp
#include<iostream>
#include"Task.h"
#include "pthreadpool.h"

TaskQueue::TaskQueue(){
	pthread_mutex_init(&m_mutex, NULL);
}
TaskQueue::~TaskQueue() {
	pthread_mutex_destroy(&m_mutex);
}
//增加任务函数
void TaskQueue::addTask(Task& task) {
	pthread_mutex_lock(&m_mutex);
	m_queue.push(task);
	printf("add task\n");
	pthread_mutex_unlock(&m_mutex);

}
//取出任务函数
Task TaskQueue::takeTask() {
	Task t;
	pthread_mutex_lock(&m_mutex);
	t= m_queue.front();
	m_queue.pop();
	printf("threadID:%ld get task\n", pthread_self());
	pthread_mutex_unlock(&m_mutex);
	return t;
}
//获取队列任务个数
int TaskQueue::getSize() {
	int s;
	pthread_mutex_lock(&m_mutex);
	s = m_queue.size();
	pthread_mutex_unlock(&m_mutex);
	return s;
}
cpp 复制代码
//Task.h
#pragma once
#include <queue>
#include <pthread.h>

using callback = void(*)(void*);
struct Task {
	//无参构造,初始化任务
	Task() {
		function = nullptr;
		arg = nullptr;
	}
	callback function;
	void* arg;
};
//任务队列类,任务队列,放任务,取任务,锁
class TaskQueue {
public:
	//初始化锁
	TaskQueue();
	//释放锁
	~TaskQueue();
	//增加任务函数
	void addTask(Task& task);
	//取出任务函数
	Task takeTask();
	//获取队列任务个数
	int getSize();

private:
	std::queue<Task> m_queue;
	pthread_mutex_t m_mutex;
};

关于头文件源文件

  • new 自定义类开内存 + 调用构造函数,受访问控制(public/private 约束)
  • new 基础类型[]只开一块内存,没有构造函数调用,不受 private 限制

关于释放堆内存析构不要判断if线程池为空

总而言之,只要执行到析构函数,对象一定不可能空

worker manager前面要加static:

关于为什么workermanager里面要重新

相关推荐
~木雨2 天前
Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背
java·面试·线程池·threadlocal·threadpool·executor
小宇宙清歡渡6 天前
SpringBoot JDK17 线程池的使用
springboot·线程池·jdk17
AI人工智能+电脑小能手13 天前
大白话说Java设计模式-37-中介者模式(源码剖析篇)
java·设计模式·线程池·中介者模式·源码分析·executor·applicationeventmulticaster
JacksonMx16 天前
CompletableFuture 生产环境踩坑详解与解决方案
spring boot·spring·线程池
lisin-lee-cooper1 个月前
简单聊聊线程池源码
java·线程池
2401_858286111 个月前
OS82.【Linux】设计线程池
java·linux·运维·服务器·开发语言·算法·线程池
小当家.1051 个月前
工具并行调用原理与实现:CompletableFuture 实战
java·agent·线程池·工具·并行
AaronJonah1 个月前
企业级线程池 traceId 闭环:上下文传递与全链路追踪实战
java·线程池·traceid跨线程传递
xiaoye-duck1 个月前
《Linux系统编程》Linux 系统多线程(八): C++ 高并发线程池全链路深度解析与从零手撕实现
linux·c++·线程池