C++线程池系列——CachedThreadPool

目录

1. CachedThreadPool简介

2. CachedThreadPool源码

3. 测试代码

4. 后记


大伙看完实现觉得有帮助的话,给点个免费的Star呗,这是项目链接,拜托拜托。

1. CachedThreadPool简介

CachedThreadPool是一种可变大小的线程池,但是用户不可以手动调整线程池内的线程数量。该线程池特点如下:

  • 在提交任务时,如无空闲线程则自动向线程池中添加新的线程。
  • 线程池内的线程具有存活时间,如果在存活时间内没有取到任务,该线程就会退出。
  • submit函数和stop函数的含义同FixedThreadPool,功能不再描述。可以去看上一篇博客的内容。
  • 用户可以在构造函数中指定最小线程数量、最大线程数量以及线程存活时间。

本文的实现的关键思路如下:

  • 使用map<id,thread>来存放线程,使用线程安全队列来存储即将退出的线程id
  • 一个额外的cleaner线程,每当有一个线程退出时,就将该线程id存入线程安全队列中,cleaner线程就被唤醒,清理那些即将退出的线程。
  • 每次提交任务时,检查空闲线程数量以及最大线程数,判断是否需要创建新的线程

剩余功能和FixedThreadPool类似,不再赘述。

2. CachedThreadPool源码

头文件如下

class CachedThreadPool {
public:
	using Task = std::function<void()>;
	CachedThreadPool(size_t corePoolSize = 0, size_t maxPoolSize = 1024, std::chrono::milliseconds keepAliveTime = std::chrono::milliseconds(1000));
	virtual ~CachedThreadPool();

	template<typename F, typename... Args>
	auto submit(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>>;
	void stop(bool wait = true);
	size_t core_workers_num()const;
	size_t max_workers_num()const;
	size_t workers_num()const;
	size_t idle_workers_num()const;
	size_t tasks_num()const{std::lock_guard<std::mutex> lock(taskMutex_);return tasks_.size();}
private:
	std::chrono::milliseconds keepAliveTime_; // 线程存活时间
	std::atomic<size_t> coreWorkersNum_; // 核心线程数
	std::atomic<size_t> maxWorkersNum_; // 最大线程数
	std::atomic<size_t> idleWorkersNum_; // 空闲线程数
	mutable std::mutex workersMutex_;
	std::unordered_map<std::thread::id, std::thread> workers_; // 用来存放线程
	
	// 用于清理即将退出的线程
	details::threadsafe_queue<std::thread::id> exitingWorkers_; // 存放要退出的线程id
	std::thread exiting_worker_cleaner_; // 清理空闲线程的线程
	std::mutex cleanerMutex_;
	std::condition_variable cleaner_cond_;


	std::vector<Task> tasks_; // 任务队列
	mutable std::mutex taskMutex_;
	std::condition_variable task_cond_;
	std::atomic<bool> isTerminated_{ false };
	std::atomic<bool> ifWait_{ true };

	void worker_loop_();
	void cleaner_loop_();
	void check_and_add_worker_();
};




template<typename F, typename... Args>
auto CachedThreadPool::submit(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>> {
	using return_type = std::invoke_result_t<F, Args...>;

	auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
	std::future<return_type> res = task->get_future();

	check_and_add_worker_();

	{
		std::lock_guard<std::mutex> lock(taskMutex_);
		tasks_.emplace_back([task]() { (*task)(); });
	}


	task_cond_.notify_one();

	return res;
}

源文件如下:

#include "CachedThreadPool.h"

namespace ThreadPools {

size_t CachedThreadPool::core_workers_num() const { return coreWorkersNum_.load(); }
size_t CachedThreadPool::max_workers_num() const { return maxWorkersNum_.load(); }
size_t CachedThreadPool::workers_num() const {
	std::lock_guard<std::mutex> lock(workersMutex_);
	return workers_.size();
}
size_t CachedThreadPool::idle_workers_num() const { return idleWorkersNum_.load(); }

CachedThreadPool::CachedThreadPool(size_t corePoolSize, size_t maxPoolSize, std::chrono::milliseconds keepAliveTime) {
	coreWorkersNum_.store(corePoolSize);
	maxWorkersNum_.store(maxPoolSize);
	keepAliveTime_ = keepAliveTime;
	idleWorkersNum_.store(0);

	for (size_t i = 0; i < coreWorkersNum_.load(); ++i) {
		std::thread t(&CachedThreadPool::worker_loop_, this);

		std::lock_guard<std::mutex> lock(workersMutex_);
		workers_[t.get_id()] = std::move(t);
		idleWorkersNum_.fetch_add(1);
	}

	exiting_worker_cleaner_ = std::thread(&CachedThreadPool::cleaner_loop_, this);
}

CachedThreadPool::~CachedThreadPool() {
	stop(true);
}

void CachedThreadPool::stop(bool wait) {
	ifWait_.store(wait);
	isTerminated_.store(true);
	task_cond_.notify_all();

	for (auto& worker : workers_) {
		if (worker.second.joinable()) {
			worker.second.join();
		}
	}
	if(exiting_worker_cleaner_.joinable())
		exiting_worker_cleaner_.join();
}

void CachedThreadPool::worker_loop_() {
	Task task;
	while (true) {
		{
			std::unique_lock<std::mutex> lock(taskMutex_);
			if (false == task_cond_.wait_for(lock, keepAliveTime_, [this]() { return isTerminated_.load() || !tasks_.empty(); })) {
				// 如果超时,先判断线程数量是否小于最小值
				std::lock_guard<std::mutex> lock(workersMutex_);
				if (workers_.size() > coreWorkersNum_.load())
					break;
				else
					continue;
			}
			// 如果线程池结束了
			if (isTerminated_.load()) {
				// 如果任务队列为空,则退出
				// 如果不为空并且不想等待,则退出
				if (tasks_.empty() || !ifWait_.load()) {
					break;
				}
			}
			// 能执行到这里,说明任务队列不为空
			task = std::move(tasks_.back());
			tasks_.pop_back();
		}
		idleWorkersNum_.fetch_sub(1);
		task();
		idleWorkersNum_.fetch_add(1);
	}
	// 线程退出时,将自己加入退出队列,等待cleaner线程处理
	idleWorkersNum_.fetch_sub(1);
	exitingWorkers_.push(std::this_thread::get_id());
	cleaner_cond_.notify_one();
}

void CachedThreadPool::cleaner_loop_() {
	std::thread t;
	std::thread::id id;
	while (true) {
		{
			std::unique_lock<std::mutex> lock(cleanerMutex_);
			cleaner_cond_.wait(lock, [this]() { return isTerminated_.load() || !exitingWorkers_.empty(); });
			if (isTerminated_.load()) {
				return;
			}
		}
		
		// 因为它是线程安全的,所以不需要加锁
		exitingWorkers_.wait_and_pop(id);
		
		{
			std::lock_guard<std::mutex> lock(workersMutex_);
			t = std::move(workers_[id]);
			workers_.erase(id);
		}
		if (t.joinable())
			t.join();
	}
}


void CachedThreadPool::check_and_add_worker_() {
	
	{
		std::lock_guard<std::mutex> lock(workersMutex_);
		if (idleWorkersNum_.load() > 0 || workers_.size() >= maxWorkersNum_.load())
		{
			
			return;
		}
	}

	std::thread t(&CachedThreadPool::worker_loop_, this);
	{

		std::lock_guard<std::mutex> lock(workersMutex_);
		workers_[t.get_id()] = std::move(t);
	}

	idleWorkersNum_.fetch_add(1);
}

} // namespace ThreadPools

下面是线程安全队列的代码:

#pragma once

#include <queue>
#include <mutex>
#include <condition_variable>
#include <type_traits>

namespace ThreadPools {
namespace details {

template<typename T>
class threadsafe_queue {
public:
	threadsafe_queue() = default;
	threadsafe_queue(const threadsafe_queue&) = delete;
	threadsafe_queue& operator=(const threadsafe_queue&) = delete;

	template<typename U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
	void push(U&& item) {
		std::lock_guard<std::mutex> lock(mtx_);
		q_.push(std::forward<U>(item));
		notEmpty_cond_.notify_one();
	}

	bool try_pop(T& item) {
		std::lock_guard<std::mutex> lock(mtx_);
		if (q_.empty()) {
			return false;
		}
		item = std::move(q_.front());
		q_.pop();
		return true;
	}

	void wait_and_pop(T& item) {
		std::unique_lock<std::mutex> lock(mtx_);
		notEmpty_cond_.wait(lock, [this] { return !q_.empty(); });
		item = std::move(q_.front());
		q_.pop();
	}

	bool empty()const {
		std::lock_guard<std::mutex> lock(mtx_);
		return q_.empty();
	}

	size_t size()const {
		std::lock_guard<std::mutex> lock(mtx_);
		return q_.size();
	}
	
private:
	std::queue<T> q_;
	mutable std::mutex mtx_;
	std::condition_variable notEmpty_cond_;
};


} // namespace details
} // namespace ThreadPools

3. 测试代码

可以使用一下代码进行测试

#include <iostream>
#include <thread>
#include <cstdio>

#include "fixedThreadPool.h"
#include "CachedThreadPool.h"

using namespace std;

void task(int id) {
	for (int i = 0; i < 10; i++)
	{
		printf("Task %d, threadid: %d, iteration %d\n", id, std::this_thread::get_id(), i);
		std::this_thread::sleep_for(std::chrono::milliseconds(500));
	}
}

#define TASK_NUM 1000

int main()
{
	ThreadPools::CachedThreadPool pool(4, 1024, std::chrono::milliseconds(10000));

	for (int i = 0; i < TASK_NUM; i++) {
		pool.submit(task, i);
	}
	std::this_thread::sleep_for(std::chrono::milliseconds(5000));
	while (true)
	{
		printf("-------tasks:%d, workers: %d, idle threads: %d ----------------\n",pool.tasks_num(), pool.workers_num(), pool.idle_workers_num());
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	}


	return 0;
}

大约二十多秒后的运行截图如下:

4. 后记

后面将会发布其他线程池的实现,敬请期待。如果有写的不太好的地方,敬请批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值