一文带你使用c++实现线程池!!!

连接

https://gitee.com/lzhiqiang1999/thread-pool
欢迎star

一 线程池

1 含义
线程池就是把一堆线程进行统一管理,避免线程过多带来的资源浪费

2 为什么要使用线程池

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性

3 什么地方可以使用线程池

(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换
(2)IO密集型的任务。因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务。
(3)计算密集型任务。线程池中的线程数设置得少一些,减少线程上下文的切换。
(4)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦

二 C++实现一个简单的线程池

1 流程示意图
在这里插入图片描述
2 组件
(1)任务队列类
(2)线程池类

3 任务队列类

  • 管理任务
    任务的组成:
    - 函数指针, 指向一个功能函数, 这函数中的操作最终被子线程处理
    - arg: 给回调函数传参
#include <queue>
#include <pthread.h>
#include <string>
#include <iostream>
//
 任务结构体
struct Task
{
	Task()
	{
		function = nullptr;
		arg = nullptr;
	}
	void *(*function)(void *);          /* 函数指针,回调函数 */
	void *arg;                          /* 上面函数的参数 */
};

typedef void* (*callback)(void*);
// 任务队列类
class TaskQueue
{
public:
	TaskQueue();
	~TaskQueue();
	// 往任务队列中添加任务
	void addTask(Task &task);
	void addTask(callback func, void* arg);

	// 获取当前队列中的任务数量
	inline size_t taskNumber()
	{
		return m_queue.size();
	}

	// 从队列中取出一个任务
	Task takeTask();

private:
	pthread_mutex_t m_mutex;	// 互斥锁,多个线程可能会同时操作任务队列
	std::queue<Task> m_queue;
};

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);
	pthread_mutex_unlock(&m_mutex);
}

void TaskQueue::addTask(callback func, void * arg)
{
	pthread_mutex_lock(&m_mutex);
	Task t;
	t.arg = arg;
	t.function = func;
	m_queue.push(t);
	pthread_mutex_unlock(&m_mutex);
}

Task TaskQueue::takeTask()
{
	if (m_queue.size() > 0)
	{
		pthread_mutex_lock(&m_mutex);
		Task t = m_queue.front();
		m_queue.pop();
		pthread_mutex_unlock(&m_mutex);
		return t;
	}
	return Task();
}

4 线程池类

  • 管理了多个线程
    • 管理者线程 :1个
      • 控制工作线程的个数
      • 发现快不够用了: 创建一批新的线程
      • 不工作的线程太多了: 销毁一批
    • 工作的线程 :n个
      • 任务来自于任务队列
      • 工作的线程处理逻辑:
        • 不停的读任务队列
          • 为空: 工作线程阻塞 -> 使用条件变量
          • 不为空: 从任务队列中取出一个任务
            • struct Task -> 任务的类型
            • 调用Task中对应的函数, 进行逻辑处理
  • 当线程阻塞了, 什么时候唤醒?
    • 往任务队列中添加任务的时候唤醒阻塞线程即可
#include <pthread.h>
#include "ThreadPool.h"
#include "TaskQueue.h"
#include <iostream>

const int DEFAULT_TIME = 10;                /*10s检测一次*/
const int MIN_WAIT_TASK_NUM = 10;           /*如果queue_size > MIN_WAIT_TASK_NUM 添加新的线程到线程池*/
const int DEFAULT_THREAD_VARY = 10;         /*每次创建和销毁线程的个数*/

// 线程池类
class ThreadPool
{
public:
	// min: 最少线程个数, max: 最大线程个数
	ThreadPool(int min, int max);
	~ThreadPool();
	// 给线程池添加任务
	void addPoolTask(Task &task);
private:
	// 判断线程是否还存活着
	bool threadIsAlive(pthread_t tid);
	// 工作线程回调函数
	void* threadWorking(void* arg);

	// 管理者线程回调函数
	void* threadManager(void* arg);

private:

	pthread_mutex_t lock;               /* 用于锁住当前类成员 */
	pthread_mutex_t thread_counter;     /* 记录忙状态线程个数de琐 -- busy_thr_num */
	pthread_cond_t queue_not_empty;     /* 任务队列里不为空时,通知等待任务的线程 */

	pthread_t *threads;                 /* 存放线程池中每个线程的tid。数组 */
	pthread_t adjust_tid;               /* 存管理线程tid */
	TaskQueue *task_queue;				/* 任务队列 */

	int min_thr_num;                    /* 线程池最小线程数 */
	int max_thr_num;                    /* 线程池最大线程数 */
	int live_thr_num;                   /* 当前存活线程个数 */
	int busy_thr_num;                   /* 忙状态线程个数 */
	int wait_exit_thr_num;              /* 要销毁的线程个数 */
};

(1)初始化

  1. 最多线程,最少线程
  2. 创建最少线程
  3. 创建管理者线程
ThreadPool::ThreadPool(int min, int max)
{
	// 实例化任务队列
	task_queue = new TaskQueue;
	do 
	{
		min_thr_num = min;
		max_thr_num = max;
		busy_thr_num = 0;
		live_thr_num = min_thr_num;               /* 活着的线程数 初值=最小线程数 */

		/* 根据最大线程上限数, 给工作线程数组开辟空间, 并清零 */
		threads = new pthread_t[sizeof(pthread_t) * max_thr_num];
		if (threads == NULL) 
		{
			break;
		}
		memset(threads, 0, sizeof(pthread_t) * max_thr_num);

		/* 初始化互斥琐、条件变量 */
		if (pthread_mutex_init(&(lock), NULL) != 0
			|| pthread_mutex_init(&(thread_counter), NULL) != 0
			|| pthread_cond_init(&(queue_not_empty), NULL) != 0)
		{
			break;
		}

		/* 启动 min_thr_num 个 work thread */
		for (int i = 0; i < min_thr_num; i++) 
		{
			// 创建并启动工作的线程
			pthread_create(&threads[i], NULL, threadWorking, this);
			string log = "start thread: " + to_string((unsigned int)threads[i]);

		}
		// 创建并启动管理者线程
		pthread_create(&(adjust_tid), NULL, threadManager, this);
	} while (0);
}

(2)添加任务

void ThreadPool::addPoolTask(Task & task)
{
	/*添加任务到任务队列里*/
	task_queue->addTask(task);
	/*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/
	pthread_cond_signal(&queue_not_empty);
}

(3)管理者线程的回调函数

  1. 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时。
  2. 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时。
void * ThreadPool::threadManager(void * arg)
{
	ThreadPool *pool = (ThreadPool*)arg;
	while (true) 
	{
		sleep(DEFAULT_TIME);                                    /*定时 对线程池管理*/
		pthread_mutex_lock(&(pool->lock));
		size_t queue_size = pool->task_queue->taskNumber();        /* 关注 任务数 */
		int live_thr_num = pool->live_thr_num;                  /* 存活 线程数 */
		pthread_mutex_unlock(&(pool->lock));

		pthread_mutex_lock(&(pool->thread_counter));
		int busy_thr_num = pool->busy_thr_num;                  /* 忙着的线程数 */
		pthread_mutex_unlock(&(pool->thread_counter));

		/* 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时 如:30>=10 && 40<100*/
		if (queue_size >= MIN_WAIT_TASK_NUM && 
			live_thr_num < pool->max_thr_num) 
		{
			pthread_mutex_lock(&(pool->lock));
			int add = 0;

			/*一次增加 DEFAULT_THREAD 个线程*/
			for (int i = 0; i < pool->max_thr_num && add < DEFAULT_THREAD_VARY
				&& pool->live_thr_num < pool->max_thr_num; i++) 
			{
				if (pool->threads[i] == 0 || !pool->threadIsAlive(pool->threads[i]))
				{
					pthread_create(&(pool->threads[i]), NULL, threadWorking, arg);
					add++;
					pool->live_thr_num++;
				}
			}

			pthread_mutex_unlock(&(pool->lock));
		}

		/* 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时*/
		if ((busy_thr_num * 2) < live_thr_num  &&  live_thr_num > pool->min_thr_num) 
		{
			/* 一次销毁DEFAULT_THREAD个线程, 隨機10個即可 */
			pthread_mutex_lock(&(pool->lock));
			pool->wait_exit_thr_num = DEFAULT_THREAD_VARY;      /* 要销毁的线程数 设置为10 */
			pthread_mutex_unlock(&(pool->lock));

			for (int i = 0; i < DEFAULT_THREAD_VARY; i++) 
			{
				/* 通知处正在等待的线程, 他们会自行终止*/
				pthread_cond_signal(&(pool->queue_not_empty));
			}
		}
	}

	return nullptr;
}

(4)工作线程线程回调函数

  1. 循环。查看任务队列此时有没有任务,没有就阻塞在条件变量。
  2. 如果有需要被删除的线程,退出当前线程。
  3. 获取任务并执行。
void * ThreadPool::threadWorking(void * arg)
{
	ThreadPool *pool = (ThreadPool *)arg;

	while (true) {
		/* Lock must be taken to wait on conditional variable */
		/*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/
		pthread_mutex_lock(&(pool->lock));

		/*task number == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/
		while (pool->task_queue->taskNumber() == 0) 
		{
			string log = "thread:" + to_string((unsigned int)pthread_self()) + " is waiting";
			pool->m_log->Log(log, __FILE__, __LINE__);
			pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));

			/*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
			if (pool->wait_exit_thr_num > 0) 
			{
				pool->wait_exit_thr_num--;

				/*如果线程池里线程个数大于最小值时可以结束当前线程*/
				if (pool->live_thr_num > pool->min_thr_num) 
				{
					log = "thread:" + to_string((unsigned int)pthread_self()) + " is exiting";
					pool->m_log->Log(log, __FILE__, __LINE__);
					pool->live_thr_num--;
					pthread_mutex_unlock(&(pool->lock));
					pthread_exit(NULL);
				}
			}
		}
		pthread_mutex_unlock(&(pool->lock));

		/*从任务队列里获取任务, 是一个出队操作*/
		Task task = pool->task_queue->takeTask();
		/*执行任务*/
		string log = "thread:" + to_string((unsigned int)pthread_self()) + " is start working...";
		pool->m_log->Log(log, __FILE__, __LINE__);
		pthread_mutex_lock(&(pool->thread_counter));                            /*忙状态线程数变量琐*/
		pool->busy_thr_num++;                                                   /*忙状态线程数+1*/
		pthread_mutex_unlock(&(pool->thread_counter));
		// 执行处理动作
		(*(task.function))(task.arg);                                           /*执行回调函数任务*/
		//task.function(task.arg);                                              /*执行回调函数任务*/

		/*任务结束处理*/
		log = "thread:" + to_string((unsigned int)pthread_self()) + " is end working...";
		pool->m_log->Log(log, __FILE__, __LINE__);
		pthread_mutex_lock(&(pool->thread_counter));
		pool->busy_thr_num--;                                       /*处理掉一个任务,忙状态数线程数-1*/
		pthread_mutex_unlock(&(pool->thread_counter));
	}

	pthread_exit(NULL);
	return nullptr;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值