【手写线程池(一)】项目介绍和代码展示

熟悉了C++基础后,这一次,我们来试着写一个项目 线程池,该项目不依赖于系统API函数,而是以C++语言为基础,能够很好的实现跨平台,更加深入的了解多线程编程、线程管理等概念

  • 😸开发环境:vs2022,C++17标准; ubuntu20.04编译so动态库
  • 😻第一版:没有直接使用C++17的any类型和C++20的信号量semaphore等机制,而是完完全全的自己实现
  • 😽第二版:使用C++11的future和packaged_task机制使代码更加简洁,用户操作更加便捷

项目经历------手写线程池

一、项目介绍

作为五大池之一(内存池、连接池、线程池、进程池、协程池),线程池的应用非常广泛,不管是客户端程序,还是后台服务器程序,都是提高业务处理能力的必备模块。在高并发场景下,系统能够更加稳定地处理大量任务。

二、所需的知识储备

该项目用到了很多C++知识和理论,其主要有:

  • 基于C++11的面向对象编程

    • 组合、继承和多态
    • STL容器
    • 智能指针
    • 函数对象
    • 绑定器
    • 可变参模板编程
    • ...
  • 基于C++11的多线程编程

    • thread
    • mutex
    • atomic
    • condition_variable
    • unique_lock
    • ...
  • C++17的any类型和C++20的信号量semaphore

  • 多线程理论

    • 多线程基本知识
    • 线程互斥和同步
    • 原子操作
    • CAS
    • ...

三、为什么要使用线程池

(1)线程的消耗:线程真的是越多越好吗?

  • 线程的创建和销毁都是非常"重"的操作
  • 线程栈本身占用大量内存
  • 线程的上下文切换要占用大量时间
  • 大量的线程同时唤醒会使系统经常出现锯齿状负载或瞬间负载量很大而导致宕机

(2)线程池的优势

操作系统上创建和销毁线程都是很"重"的操作,耗时耗能都比较多,那么在服务执行的过程中,如果业务量比较大,实时的去创建线程、执行业务、业务完成后销毁线程,会导致系统的实时性能降低,业务处理能力也会降低。而线程池的优势就是(每个池都有自己的优势),在服务启动之初,就是先创建好线程池里面的线程,当业务流量到来时需要分配线程,直接从线程池里获取一个空闲线程执行task任务即可,task执行完成后,也不要释放线程,而是把线程归还到线程池中继续给后续的task提供服务。

(3)线程池的两种模式

  • fixed模式:线程池里面的线程个数是固定不变的,一般是ThreadPool创建时根据当前机器的CPU核心数量进程指定

  • cached模式:线程池里面的线程个数是可动态增长的,根据任务的数量动态的增加线程的数量,但是会设置一个线程数量的阈值(线程过多也不行),任务处理完毕,如果任务增长的线程空闲了一定时间还没有处理其他任务,那么关闭该线程,保持池中最初数量的线程

四、项目设计图浏览

图中我们可以看到,pool.setMode(fixed(default) | cached) 用于设置线程池的工作模式是fixed,还是cached,用户可以根据一定的语法规则提交异步任务,即pool.submitTask(concreteTask),同时通过自己实现的semaphore机制,异步获取任务处理结果result.get().Cast_<结果类型>(),即当任务执行完毕时,result.get()从 wait 状态转化为就绪状态,进而调用Cast_<结果类型>()来获取任务执行完毕的返回值。任务的提交和线程的创建通过mutex + condition_variable实现

五、代码展示

(第一版)


➡️threadpool.h 头文件


cpp 复制代码
#ifndef THREADPOOL_H
#define THREADPOOL_H

#include<vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>
#include<functional>
#include<thread>
#include<unordered_map>
//using namespace std;

//any类型: 可以接收任意类型
class Any
{
public:
	Any() = default;
	~Any() = default;
	Any(const Any&) = delete;
	Any& operator=(const Any&) = delete;
	Any(Any&&) = default; //默认调用成员变量的右值引用的...
	Any& operator=(Any&&) = default; // ...

	//这个构造函数可以让Any类型接收任何数据
	template<typename T>
	Any(T data) : base_(std::make_unique<Derive<T>>(data)) {}
	//new Derive<T>(data)

	//存储的data数据取出
	template<typename T>
	T Cast_()
	{
		//怎么从base_找到它所指向了Derive对象,从中取出data变量

		//基类指针 -> 派生类指针
		Derive<T>* pd = dynamic_cast<Derive<T>*>( base_.get());
		if (pd == nullptr)
		{
			throw "type is unmatch!";
		}
		return pd->data_;
	}

private:
	class Base  //基类类型
	{
	public:
		virtual ~Base() = default; 
	};

	//派生类类型
	template<typename T>
	class Derive :public Base
	{
	public:
		Derive(T data) :data_(data) {}

		T data_;  //保持了任意的其他类型
	};

private:
	std::unique_ptr<Base> base_; //定义一个基类指针

};

//信号量
class Semaphore
{
public:
	Semaphore(int limit = 0) : resLimit_(limit) {}
	~Semaphore() = default;

	void wait()   //获取一个信号量资源
	{
		std::unique_lock<std::mutex> lock(mtx_);
		//等待信号量资源,没有则阻塞
		cond_.wait(lock, [&]()->bool {return resLimit_ > 0; });
		resLimit_--;
	}

	void post()  //增加一个信号量资源
	{
		std::unique_lock<std::mutex> lock(mtx_);
		resLimit_++;
		//Linux下condition_variable的析构函数什么也没做,导致这里状态失效,无故阻塞
		cond_.notify_all();  
	}

private:
	int resLimit_;
	std::mutex mtx_;
	std::condition_variable cond_;
};

class Task; //Task类型的前置声明

//实现接收提交到线程池的task任务指向完成后的返回值类型Result
class Result
{
public:
	Result(std::shared_ptr<Task> task, bool isvalid = true);
	~Result() = default;

	//问题一:setVal,获取任务执行完的返回值
	void setVal(Any any);

	//问题二:get,用户调用方法获取其task的返回值
	Any get();

private:
	Any any_; //存储任务的返回值
	Semaphore sem_;  //线程通信信号量
	std::shared_ptr<Task> task_;  //指向对应获取返回值的任务对象
	std::atomic_bool isValid_;   //返回值是否有效
};

//任务抽象基类
class Task
{
public:
	Task();
	~Task() = default;

	void exec();
	void setResult(Result* res);

	//用户可以自定义任务类型,从Task继承,重写run方法
	virtual Any run() = 0;
private:
	Result* result_; //Result对象的生命周期 > task
};

//线程池的工作模式fixed、cached
enum class PoolMode{MODE_FIXED, MODE_CACHED}; 

/*  
example: 
ThreadPool pool;
pool.start(4);

class MyTask:public Task
{
public:
	void run() {...}
};

pool.submitTask(std::make_shared<MyTask>());

*/

class Thread //线程类型
{
public:
	using ThreadFunc = std::function<void(int)>;

	Thread(ThreadFunc);  //线程构造

	~Thread(); //线程析构

	void start(); //启动线程

	int getId() const;//获取线程id

private:
	ThreadFunc func_;
	static int generateId_;  
	int threadId_;   //保存线程id,用于回收...
};

class ThreadPool //线程池类型
{
public:
	ThreadPool(); //构造
	~ThreadPool(); //析构

	void start(int initThreadSize = std::thread::hardware_concurrency()); //开启线程池

	void setMode(PoolMode mode); //工作模式

	void setTaskQueMaxThreshHold(int threshold); //设置任务队列的阈值

	void setThreadSizeThreshHold(int threshold);  //设置线程个数阈值

	Result submitTask(std::shared_ptr<Task> sp); //给线程池提交任务

	ThreadPool(const ThreadPool&) = delete;  //禁用拷贝构造
	ThreadPool& operator=(const ThreadPool&) = delete; //禁用赋值重载

private:
	//定义线程函数
	void threadFunc(int threadid);

	//检查线程池的工作状态
	bool checkRunningState() const; 

private:
	std::unordered_map<int, std::unique_ptr<Thread>> threads_; //线程列表
	size_t initThreadSize_;  //初始的线程数量
	std::atomic_int idleThreadSize_;//记录空闲线程的数量
	std::atomic_int curThreadSize_;   //记录线程池里的线程总数量
	int threadSizeThreshHold_;  //线程数量上限阈值

	std::queue<std::shared_ptr<Task>> taskQue_;  //任务队列
	std::atomic_uint taskSize_;  //任务的数量
	int taskQueMaxThreshold_;  //任务队列上限阈值

	std::mutex taskQueMtx_;  //保证任务队列的线程安全
	std::condition_variable notFull_;  //任务队列不满
	std::condition_variable notEmpty_; //任务队列不空
	std::condition_variable exitCond_; // 等待线程资源全部回收

	PoolMode poolMode_;  //当前线程池的工作模式

	std::atomic_bool isPoolRunning_; //表示当前线程池的启动状态

};

#endif

➡️threadpool.cpp 源文件


cpp 复制代码
#include"threadpool.h"
#include<iostream>
#include<chrono>

const int TASK_MAX_THRESHHOLD = INT32_MAX;
const int THREAD_MAX_THRESHHOLD = 100;
const int THREAD_MAX_IDLE_TIME = 60;  //单位:秒

ThreadPool::ThreadPool() :  //构造
	initThreadSize_(4),
	taskSize_(0),
	idleThreadSize_(0),
	curThreadSize_(0),
	taskQueMaxThreshold_(TASK_MAX_THRESHHOLD),
	threadSizeThreshHold_(THREAD_MAX_THRESHHOLD),
	poolMode_(PoolMode::MODE_FIXED),
	isPoolRunning_(false)
{}

ThreadPool::~ThreadPool()   //析构
{
	isPoolRunning_ = false;
	//等待线程池里的线程返回 两种: 1.阻塞 2.执行任务中
	std::unique_lock<std::mutex> lock(taskQueMtx_);
	notEmpty_.notify_all();

	exitCond_.wait(lock, [&]() {return threads_.size() == 0; });
}

void ThreadPool::start(int initThreadSize)  //开启线程池
{
	isPoolRunning_ = true;  //设置运行状态
	initThreadSize_ = initThreadSize; //初始线程个数
	curThreadSize_ = initThreadSize_;

	//创建线程对象
	for (int i = 0; i < initThreadSize_; i++)
	{
		//创建thread线程对象的时候,把线程函数给到thread线程对象
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));  
		这种方式允许 Thread 对象通过threadFunc 访问 ThreadPool 类的成员变量和成员函数,从而实现对线程池内部状态和功能的访问。
		
		// 
		//new Thread(std::bind(&ThreadPool::threadFunc
		//threads_.emplace_back(std::move(ptr));  //资源转移
		int threadId = ptr->getId();
		threads_.emplace(threadId, std::move(ptr));
	}

	//启动所有线程  std::vector<>
	for (int i = 0; i < initThreadSize_; i++)
	{
		threads_[i]->start(); //需要执行一个线程函数
		idleThreadSize_++; //记录初始空闲线程的数量
	}

}

void ThreadPool::setMode(PoolMode mode)  //工作模式
{
	if (checkRunningState()) 
		return;

	poolMode_ = mode;
}

void ThreadPool::setTaskQueMaxThreshHold(int threshhold) //设置任务队列阈值
{
	taskQueMaxThreshold_ = threshhold;
}

void ThreadPool::setThreadSizeThreshHold(int threshold)
{
	if (checkRunningState())
		return;
	if (poolMode_ == PoolMode::MODE_CACHED)
	{
		threadSizeThreshHold_ = threshold;
	}
}

Result ThreadPool::submitTask(std::shared_ptr<Task> sp)  //提交任务
{
	//获取锁
	std::unique_lock<std::mutex> lock(taskQueMtx_);

	//线程通信,等待任务队列有空余
	/*while (taskQue_.size() == taskQueMaxThreshold_)
	{
		notFull_.wait(lock);
	}*/

	// C++11  wait  wait_for  wait_until
//	notFull_.wait(lock, [&]()->bool {return taskQue_.size() < taskQueMaxThreshold_; });

	//用户提交任务,不能超过1s,否则判断失败,返回
	if (!notFull_.wait_for(lock, std::chrono::seconds(1),
		[&]()->bool {return taskQue_.size() < (size_t)taskQueMaxThreshold_; }))
	{
		//表示notfull_等待1s,依然没有满足,失败
		std::cerr << "task quue is full, submit task fail" << std::endl;
		return Result(sp, false);
	}

	//如果有空余,把任务队列放入
	taskQue_.emplace(sp);
	taskSize_++;

	//通知 notEmpty()
	notEmpty_.notify_all();

	//cached模式 任务处理比较紧急 场景:小而快   需要根据相应的任务数量和空闲线程的数量,看是否需要创建新的线程
	if (poolMode_ == PoolMode::MODE_CACHED
		&& taskSize_ > idleThreadSize_
		&& curThreadSize_ < threadSizeThreshHold_)
	{
		std::cout << "create new thread: ..." << std::endl;

		//创建新线程
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
		int threadId = ptr->getId();
		threads_.emplace(threadId, std::move(ptr));
		threads_[threadId]->start(); //启动线程
		//修改线程个数相关的变量
		idleThreadSize_++;
		curThreadSize_++;
	}

	//返回任务的Result对象
	//return task->getResult();
	return Result(sp);
}


void ThreadPool::threadFunc(int threadid) //定义线程函数
{
	auto lastTime = std::chrono::high_resolution_clock().now();

	//等待所有任务必须执行完,线程池才可以开始回收资源 
	for(;;)   //当析构函数置为false时,线程刚好进入while    《----    死锁问题分析  !!!
	{
		std::shared_ptr<Task> task;
		{
			//先获取锁
			std::unique_lock<std::mutex> lock(taskQueMtx_);    //一、pool线程先获取锁,这里阻塞,等pool析构进入wait,这里能够获取锁
															//执行到 第187行,没有人唤醒   -》  死锁
																//二、 线程池里的线程先获取锁,在188行 等待被唤醒,释放锁,pool线程也在wait
																//并没有唤醒线程池里的线程

			std::cout << "tid: " << std::this_thread::get_id();
			std::cout << "尝试获取任务... " << std::endl;

			
			/*while (taskQue_.empty())
			{
				notEmpty_.wait(lock);
			}*/

			//cached模式,可能创建了很多线程,但空闲时间超过60s,应该把多余的线程回收
			//每1秒返回一次  ->  超时返回,还是有任务待执行返回
			//锁 + 双重判断 !!!!!!!!!!!!!

				while (taskQue_.size() == 0)
				{
					//线程池要结束了,回收线程资源
					if (!isPoolRunning_)
					{
						threads_.erase(threadid);
						std::cout << "threadid: " << std::this_thread::get_id() << " exit!" << std::endl;
						exitCond_.notify_all();
						return ;  //线程函数结束,线程结束
					}

					if (poolMode_ == PoolMode::MODE_CACHED)
					{
						//条件变量超时返回
						if (std::cv_status::timeout ==
							notEmpty_.wait_for(lock, std::chrono::seconds(1)))
						{
							auto now = std::chrono::high_resolution_clock::now();
							auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
							if (dur.count() >= THREAD_MAX_IDLE_TIME
								&& curThreadSize_ > initThreadSize_)
							{
								//开始回收当前线程
								//记录线程数量的相关变量的值修改 
								//把线程对象从线程列表容器中删除  没有办法匹配 threafFunc <=> thread对象
								//threadid => thread对象 -> 删除
								threads_.erase(threadid);
								curThreadSize_--;
								idleThreadSize_--;

								std::cout << "threadid: " << std::this_thread::get_id() << " exit!" << std::endl;
								return;
							}
						}
					}
					else
					{
						//等待notEmpty
						notEmpty_.wait(lock);
					}

					线程池要结束了,回收线程资源
					//if (!isPoolRunning_)
					//{
					//	threads_.erase(threadid);
					//	//curThreadSize_--;
					//	//idleThreadSize_--;
					//	std::cout << "threadid: " << std::this_thread::get_id() << " exit!" 
					//		<< std::endl;
					//	exitCond_.notify_all();
					//	return;
					//}
				}
			
			idleThreadSize_--;

			std::cout << "tid: " << std::this_thread::get_id();
			std::cout << "获取任务成功... " << std::endl;

			//从任务队列中取出一个任务
			task = taskQue_.front();
			taskQue_.pop();
			taskSize_--;

			//如果依然有任务,继续通知其他线程执行任务
			if (taskQue_.size() > 0)
			{
				notEmpty_.notify_all();
			}

			//通知可以继续提交生产任务
			notFull_.notify_all();  

		} //不需要等待任务执行完成后再解锁,而是取出任务后直接释放,所以规定一个作用域{...}


		//当前线程负责执行
		if (task != nullptr)
		{
			task->exec();	
		}
		idleThreadSize_++;
		lastTime = std::chrono::high_resolution_clock().now();  //更新线程执行完任务的时间
	}	
}

bool ThreadPool::checkRunningState() const
{
	return isPoolRunning_;
}


  线程方法实现     /
int Thread::generateId_ = 0;

Thread::Thread(ThreadFunc func):  //构造
	func_(func),
	threadId_(generateId_++)
{}

Thread::~Thread()  //析构
{
}

void Thread::start()  //启动线程
{
	//创建一个线程来执行一个线程函数
	std::thread t(func_, threadId_);   //有线程对象t 和线程函数func_
	t.detach(); //设置分离线程 pthread_detach
}

int Thread::getId() const
{
	return threadId_;
}


///   Result方法实现   //

Result::Result(std::shared_ptr<Task> task, bool isvalid) :
	isValid_(isvalid), task_(task)
{
	task_->setResult(this);
}

Any Result::get()   //用户调用
{
	if (!isValid_)
	{
		return "";
	}

	sem_.wait();   //task任务没有执行玩,阻塞用户线程
	return std::move(any_);
}

void Result::setVal(Any any)  //
{
	// 存储task的返回值
	this->any_ = std::move(any);
	sem_.post();  //已经获取任务的返回值,增加信号量资源
}




/   task方法实现    /

Task::Task(): result_(nullptr) {}

void Task::exec()
{
	if (result_ != nullptr)
	{
		result_->setVal(run());  //发生多态调用
	}
}

void Task::setResult(Result* res)
{
	result_ = res;
}

(第二版)


➡️threadpool.h 头文件


cpp 复制代码
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include<iostream>
#include<vector>
#include<queue>
#include<memory>
#include<atomic>
#include<mutex>
#include<condition_variable>
#include<functional>
#include<thread>
#include<unordered_map>
#include<future>

const int TASK_MAX_THRESHHOLD = 2;
const int THREAD_MAX_THRESHHOLD = 100;
const int THREAD_MAX_IDLE_TIME = 60;  //单位:秒


//线程池的工作模式fixed、cached
enum class PoolMode { MODE_FIXED, MODE_CACHED };

class Thread //线程类型
{
public:
	using ThreadFunc = std::function<void(int)>;

	Thread(ThreadFunc func) :  //构造
		func_(func),
		threadId_(generateId_++)
	{}

	~Thread() = default; //线程析构

	void start()  //启动线程
	{
		//创建一个线程来执行一个线程函数
		std::thread t(func_, threadId_);   //有线程对象t 和线程函数func_
		t.detach(); //设置分离线程 pthread_detach
	}

	int getId() const
	{
		return threadId_;
	}

private:
	ThreadFunc func_;
	static int generateId_;
	int threadId_;   //保存线程id,用于回收...
};

int Thread::generateId_ = 0;

class ThreadPool //线程池类型
{
public:
	ThreadPool() :  //构造
		initThreadSize_(4),
		taskSize_(0),
		idleThreadSize_(0),
		curThreadSize_(0),
		taskQueMaxThreshold_(TASK_MAX_THRESHHOLD),
		threadSizeThreshHold_(THREAD_MAX_THRESHHOLD),
		poolMode_(PoolMode::MODE_FIXED),
		isPoolRunning_(false)
	{}
	~ThreadPool()   //析构
	{
		isPoolRunning_ = false;
		//等待线程池里的线程返回 两种: 1.阻塞 2.执行任务中
		std::unique_lock<std::mutex> lock(taskQueMtx_);
		notEmpty_.notify_all();

		exitCond_.wait(lock, [&]() {return threads_.size() == 0; });
	}

	void start(int initThreadSize)  //开启线程池
	{
		isPoolRunning_ = true;  //设置运行状态
		initThreadSize_ = initThreadSize; //初始线程个数
		curThreadSize_ = initThreadSize_;

		//创建线程对象
		for (int i = 0; i < initThreadSize_; i++)
		{
			//创建thread线程对象的时候,把线程函数给到thread线程对象
			auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
			//new Thread(std::bind(&ThreadPool::threadFunc
			//threads_.emplace_back(std::move(ptr));  //资源转移
			int threadId = ptr->getId();
			threads_.emplace(threadId, std::move(ptr));
		}

		//启动所有线程  std::vector<>
		for (int i = 0; i < initThreadSize_; i++)
		{
			threads_[i]->start(); //需要执行一个线程函数
			idleThreadSize_++; //记录初始空闲线程的数量
		}

	}


	void setMode(PoolMode mode)  //工作模式
	{
		if (checkRunningState())
			return;

		poolMode_ = mode;
	}

	void setTaskQueMaxThreshHold(int threshhold) //设置任务队列阈值
	{
		taskQueMaxThreshold_ = threshhold;
	}

	void setThreadSizeThreshHold(int threshold)
	{
		if (checkRunningState())
			return;
		if (poolMode_ == PoolMode::MODE_CACHED)
		{
			threadSizeThreshHold_ = threshold;
		}
	}

	//使用可变参模板编程,让submitTask可以接收任意任务函数和任意数量的参数
	// 1. pool.submitTask(sum, 10, 20)  引用折叠
	//Result submitTask(std::shared_ptr<Task> sp); //给线程池提交任务
	template<typename Func, typename ...Args>
	//std::future<返回值类型>用不了,需要auto 。decltype
	auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))>
	{
		//打包任务 -> 任务队列
		using RType = decltype(func(args...)); // 返回值类型
		auto task = std::make_shared<std::packaged_task<RType()>>(
			std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
		);
		std::future<RType> result = task->get_future();
		
		//获取锁
		std::unique_lock<std::mutex> lock(taskQueMtx_);

		//用户提交任务,不能超过1s,否则判断失败,返回
		if (!notFull_.wait_for(lock, std::chrono::seconds(1),
			[&]()->bool {return taskQue_.size() < (size_t)taskQueMaxThreshold_; }))
		{
			//表示notfull_等待1s,依然没有满足,失败
			std::cerr << "task quue is full, submit task fail" << std::endl;
			auto task = std::make_shared<std::packaged_task<RType()>>(
				[]()->RType {return RType(); });
			(*task)(); //需要被执行才能得到它的返回值
			return task->get_future();
		}

		//如果有空余,把任务队列放入
		//taskQue_.emplace(sp); 
		//using Task = std::function<void()>;
		taskQue_.emplace([task]()->void { (*task)();});
		taskSize_++;

		//通知 notEmpty()
		notEmpty_.notify_all();

		//cached模式 任务处理比较紧急 场景:小而快   需要根据相应的任务数量和空闲线程的数量,看是否需要创建新的线程
		if (poolMode_ == PoolMode::MODE_CACHED
			&& taskSize_ > idleThreadSize_
			&& curThreadSize_ < threadSizeThreshHold_)
		{
			std::cout << "create new thread: ..." << std::endl;

			//创建新线程
			auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
			int threadId = ptr->getId();
			threads_.emplace(threadId, std::move(ptr));
			threads_[threadId]->start(); //启动线程
			//修改线程个数相关的变量
			idleThreadSize_++;
			curThreadSize_++;
		}

		//返回任务的Result对象
		//return task->getResult();
		return result;
	}

	ThreadPool(const ThreadPool&) = delete;  //禁用拷贝构造
	ThreadPool& operator=(const ThreadPool&) = delete; //禁用赋值重载

private:
	//定义线程函数
	void threadFunc(int threadid) //定义线程函数
	{
		auto lastTime = std::chrono::high_resolution_clock().now();

		//等待所有任务必须执行完,线程池才可以开始回收资源 
		for (;;)   //当析构函数置为false时,线程刚好进入while    《----    死锁问题分析  !!!
		{
			Task task;
			{
				//先获取锁
				std::unique_lock<std::mutex> lock(taskQueMtx_);    //一、pool线程先获取锁,这里阻塞,等pool析构进入wait,这里能够获取锁
				//执行到 第187行,没有人唤醒   -》  死锁
					//二、 线程池里的线程先获取锁,在188行 等待被唤醒,释放锁,pool线程也在wait
					//并没有唤醒线程池里的线程

				std::cout << "tid: " << std::this_thread::get_id();
				std::cout << "尝试获取任务... " << std::endl;
				//cached模式,可能创建了很多线程,但空闲时间超过60s,应该把多余的线程回收

					//每1秒返回一次  ->  超时返回,还是有任务待执行返回
				//锁 + 双重判断 !!!!!!!!!!!!!
				while (taskQue_.size() == 0)
				{
					//线程池要结束了,回收线程资源
					if (!isPoolRunning_)
					{
						threads_.erase(threadid);
						std::cout << "threadid: " << std::this_thread::get_id() << " exit!" << std::endl;
						exitCond_.notify_all();
						return;  //线程函数结束,线程结束
					}

					if (poolMode_ == PoolMode::MODE_CACHED)
					{
						//条件变量超时返回
						if (std::cv_status::timeout ==
							notEmpty_.wait_for(lock, std::chrono::seconds(1)))
						{
							auto now = std::chrono::high_resolution_clock::now();
							auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
							if (dur.count() >= THREAD_MAX_IDLE_TIME
								&& curThreadSize_ > initThreadSize_)
							{
								//开始回收当前线程
								//记录线程数量的相关变量的值修改 
								//把线程对象从线程列表容器中删除  没有办法匹配 threafFunc <=> thread对象
								//threadid => thread对象 -> 删除
								threads_.erase(threadid);
								curThreadSize_--;
								idleThreadSize_--;

								std::cout << "threadid: " << std::this_thread::get_id() << " exit!" << std::endl;
								return;
							}
						}
					}
					else
					{
						//等待notEmpty
						notEmpty_.wait(lock);
					}
				}

				idleThreadSize_--;

				std::cout << "tid: " << std::this_thread::get_id();
				std::cout << "获取任务成功... " << std::endl;


				//从任务队列中取出一个任务
				task = taskQue_.front();
				taskQue_.pop();
				taskSize_--;

				//如果依然有任务,继续通知其他线程执行任务
				if (taskQue_.size() > 0)
				{
					notEmpty_.notify_all();
				}

				//通知可以继续提交生产任务
				notFull_.notify_all();

			} //不需要等待任务执行完成后再解锁,而是取出任务后直接释放,所以规定一个作用域{...}


			//当前线程负责执行
			if (task != nullptr)
			{
				task();  //执行function<void()>

			}
			idleThreadSize_++;
			lastTime = std::chrono::high_resolution_clock().now();  //更新线程执行完任务的时间
		}
	}

	//检查线程池的工作状态
	bool checkRunningState() const
	{
		return isPoolRunning_;
	}

private:
	//std::vector<std::unique_ptr<Thread>> threads_;
	std::unordered_map<int, std::unique_ptr<Thread>> threads_; //线程列表
	size_t initThreadSize_;  //初始的线程数量
	std::atomic_int idleThreadSize_;//记录空闲线程的数量
	std::atomic_int curThreadSize_;   //记录线程池里的线程总数量
	int threadSizeThreshHold_;  //线程数量上限阈值

	// Task 任务 -》 函数对象
	using Task = std::function<void()>;
	std::queue<Task> taskQue_;  //任务队列
	std::atomic_uint taskSize_;  //任务的数量
	int taskQueMaxThreshold_;  //任务队列上限阈值

	std::mutex taskQueMtx_;  //保证任务队列的线程安全
	std::condition_variable notFull_;  //任务队列不满
	std::condition_variable notEmpty_; //任务队列不空
	std::condition_variable exitCond_; // 等待线程资源全部回收

	PoolMode poolMode_;  //当前线程池的工作模式

	std::atomic_bool isPoolRunning_; //表示当前线程池的启动状态

};

#endif

➡️threadpool.cpp 源文件(也是测试文件)


cpp 复制代码
#include<iostream>
#include<functional>
#include<thread>
#include<future>
#include"threadpool.h"
using namespace std;

int sum1(int a, int b) {
	std::this_thread::sleep_for(std::chrono::seconds(3));
	return a + b;
}
int sum2(int a, int b, int c) { return a + b + c; }

int main()
{

	ThreadPool pool;
	//pool.setMode(PoolMode::MODE_CACHED);
	pool.start(2);

	future<int> r1 = pool.submitTask(sum1, 10, 20);
	future<int> r2 = pool.submitTask(sum2, 10, 20, 30);
	future<int> r3 = pool.submitTask([](int a, int b)->int {return a + b; }, 10, 20);
	future<int> r4 = pool.submitTask(sum1, 10, 20);
	future<int> r5 = pool.submitTask(sum1, 10, 20);
	future<int> r6 = pool.submitTask(sum1, 10, 20);

	cout << r1.get() << endl;
	cout << r2.get() << endl;
	cout << r3.get() << endl;
	cout << r4.get() << endl;
	
	cout << r5.get() << endl;
	cout << r6.get() << endl;
	
	//function<int(int, int)> p(sum1);
	//packaged_task<int(int, int)> task(sum1);
	//future<int> res = task.get_future();  //future -> Result
	//thread t(std::move(task), 10, 20);
	t.detach();

	//cout << res.get() << endl;  //耗时的话,会阻塞

	/*thread t1(sum1, 10, 20);
	thread t2(sum2, 10, 20, 30);
	t1.join();
	t2.join();*/

	return 0;
}

六、总结

以上就是有关于【手写线程池(一)】项目介绍和代码展示的内容,下一篇【手写线程池(二)】介绍线程池fixed模式的具体实现过程🔚🔚🔚

🌻🌻🌻如果聪明的你浏览到这篇文章并觉得文章内容对你有帮助,请不吝动动手指,给博主一个小小的赞和收藏🌻🌻🌻

相关推荐
biomooc4 分钟前
R语言/Rstudio 报错
开发语言·r语言
Theliars8 分钟前
C语言之字符串
c语言·开发语言
Root_Smile10 分钟前
【C++】类和对象
开发语言·c++
Reese_Cool11 分钟前
【数据结构与算法】排序
java·c语言·开发语言·数据结构·c++·算法·排序算法
一行玩python25 分钟前
SQLAlchemy,ORM的Python标杆!
开发语言·数据库·python·oracle
「QT(C++)开发工程师」29 分钟前
【qt版本概述】
开发语言·qt
数据小爬虫@1 小时前
利用Python爬虫获取淘宝店铺详情
开发语言·爬虫·python
高 朗1 小时前
【GO基础学习】基础语法(2)切片slice
开发语言·学习·golang·slice
寒笙LED2 小时前
C++详细笔记(六)string库
开发语言·c++·笔记
IT书架2 小时前
golang面试题
开发语言·后端·golang