C++11线程池(使用新特性)

1、三大类

  • 任务类Task
  • 任务队列TaskQueue
  • 线程池ThreadPool

2、任务类Task

#pragma once

#include <functional>
using namespace std;

/*
	1、任务类,用于封装来自用户提供的任务,将执行函数与参数封装,调用时统一使用内部的exec函数
*/
template<class T>
struct Task
{
public:
	//构造函数
	Task()
	{

	}

	Task(function<void(T)> function, T args) : m_function(function), m_args(args)
	{

	}

	//执行函数
	void exec()
	{
		m_function(m_args);
	}

private:
	function<void(T)> m_function;
	T m_args;
};
};

3、任务队列TaskQueue

#pragma once

#include "Task.h"

#include <queue>
#include <mutex>

/*
	2、任务队列,负责管理任务的容器类,并且是线程安全的,支持并发操作
*/
template<class T>
class TaskQueue
{
public:
	TaskQueue()
	{

	}

	//增加任务
	void addTask(const Task<T> & task)
	{
		m_QueMutex.lock();//加锁
		m_QueTaskQueue.push(task);	//任务入队
		m_QueMutex.unlock();//解锁
	}

	//取出任务
	Task<T> takeTask()
	{
		Task<T> t;
		m_QueMutex.lock();
		if (m_QueTaskQueue.size() > 0)
		{
			t = m_QueTaskQueue.front();
			m_QueTaskQueue.pop();
		}
		m_QueMutex.unlock();
		return t;
	}

	//获取任务队列中的任务数量
	int taskSize()
	{
		int size_now;
		m_QueMutex.lock();
		size_now = m_QueTaskQueue.size();
		m_QueMutex.unlock();
		return size_now;
	}

	//判断任务队列是否为空
	bool taskEmpty()
	{
		bool empty;
		m_QueMutex.lock();
		empty = m_QueTaskQueue.empty();
		m_QueMutex.unlock();
		return empty;
	}

	//取出和执行任务
	void executeTasks()
	{
		m_QueMutex.lock();
		if (!m_QueTaskQueue.empty())
		{
			cout << "  " << this_thread::get_id() << "\t";
			Task<T> t = m_QueTaskQueue.front();
			m_QueTaskQueue.pop();
			t.exec();
		}
		m_QueMutex.unlock();
	}

private:
	queue<Task<T>>  m_QueTaskQueue;			//链式队列(底层是数组 + 链式)

	std::mutex m_QueMutex;					//锁对象,同步链式队列对象
};

4、线程池类

#include "TaskQueue.h"

#include <iostream>
#include <condition_variable>
#include <thread>
#include <vector>
#include <functional>
#include <memory>

/*
	3、线程池类
*/
template<class T>
class ThreadPool
{
public:
	/*
		1、构造函数
			min -> 线程池中维持的最小的工作线程数
			max -> 线程池中维持的最大的工作线程数
			check_sec -> 线程池中管理者线程每个多少秒进行检测,根据实际情况负责增加或者减少工作线程数量

			构造函数初始化类成员
			创建初始的min个子线程
			创建一个管理者线程
	*/
	ThreadPool(const int &min, const int &max, int check_sec = 5)
		: m_minNum(min), m_maxNum(max), m_checkSec(check_sec),
		m_shutdown(false), m_toBeExitNum(0),
		m_aliveNum(min), m_busyNum(0)
	{
		// 初始化任务队列
		m_TaskQueue = std::make_unique<TaskQueue<T>>();
		m_threads.reserve(max); // 预留线程数量

		// 创建min个工作线程
		for (int i = 0; i < m_minNum; i++)
		{
			m_threads.emplace_back(&ThreadPool::work, this);
			std::cout << m_threads.back().get_id() << " 线程创建成功" << std::endl; // 考虑变为日志操作
		}

		// 创建一个管理者线程,负责协调负载均衡问题
		m_manager = std::thread(&ThreadPool::managerWork, this);
	}

	/*
		2、析构函数
			等待管理者线程结束
			唤醒所有阻塞中的工作线程
	*/
	~ThreadPool()
	{
		m_shutdown = true;
		m_manager.join();

		// 唤醒所有的阻塞中的线程
		for (auto &thread : m_threads)
		{
			m_cond.notify_one();
			thread.join();
		}

		cout << "线程池销毁" << endl;
	}

	/*
		3、增加任务函数
			由任务队列对象负责添加任务,然后通过条件变量通知
	*/
	void addTask(const Task<T> task)
	{
		if (m_shutdown)
			return;

		m_TaskQueue->addTask(task); // 增加任务

		m_cond.notify_one();// 唤醒阻塞等待的工作线程
	}

	/*
		4、获取忙碌中的线程数
			需要加锁
	*/
	int getBusyNum()
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		return m_busyNum;
	}

	/*
		5、获取存活着的线程数
			需要加锁
	*/
	int getAliveNum()
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		return m_aliveNum;
	}

	/*
		6、获取任务队列中的任务数量
			需要加锁
	*/
	int getTaskNum()
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		return m_TaskQueue->taskSize();
	}

private:
	/*
		7、工作线程执行的函数
			主要是取出任务然后执行
				需要考虑是否有任务以及是否需要关闭当前线程的情况
	*/
	void work()
	{
		while (true)
		{
			// 访问任务队列取出任务执行
			std::unique_lock<std::mutex> lock(m_mutex);

			// 如果进入循环说明线程池中没有任务可执行
			while (m_TaskQueue->taskEmpty() && !m_shutdown)
			{ // 没有任务阻塞
				m_cond.wait(lock);

				//退出阻塞状态,有两种情况:一种是任务队列中有新的任务;一种是需要删除部分线程
				if (m_toBeExitNum > 0)
				{
					m_toBeExitNum--;
					if (m_aliveNum > m_minNum)
					{
						m_aliveNum--;
						return; // 终止线程
					}
				}
			}

			// 判断线程池是否关闭了
			if (m_shutdown)
			{
				return; // 线程直接结束
			}

			// 开始执行任务
			Task<T> task = m_TaskQueue->takeTask();
			m_busyNum++;
			lock.unlock();	//解锁,其他线程可以拿到锁执行

			task.exec(); // 执行任务可能是个耗时操作

			lock.lock();
			m_busyNum--;
		}
	}

	/*
		8、管理者线程的工作函数
	*/
	void managerWork()
	{
		while (!m_shutdown)
		{
			// 每个m_checkSec秒检测一次
			std::this_thread::sleep_for(std::chrono::seconds(m_checkSec));

			std::unique_lock<std::mutex> lock(m_mutex);
			int taskNum = m_TaskQueue->taskSize();
			int liveNum = m_aliveNum;
			int busyNum = m_busyNum;

			// 增加线程
			if (taskNum > liveNum && liveNum < m_maxNum)
			{
				int toNum = std::min(taskNum - liveNum, m_maxNum - liveNum);
				for (int i = 0; i < toNum; i++)
				{
					m_threads.emplace_back(&ThreadPool::work, this);
					m_aliveNum++;
				}
			}

			// 减少线程数量
			if (busyNum * 2 < liveNum && liveNum > m_minNum)
			{
				int num = std::min((liveNum - m_minNum + 1) / 2, liveNum - m_minNum);
				m_toBeExitNum = num;

				lock.unlock();

				// 此时要唤醒m_toBeExitNum个线程,不能让添加任务时的信号量唤醒
				for (int i = 0; i < num; i++)
				{
					m_cond.notify_one();
				}

				//lock.lock();
			}

			lock.unlock();
		}
	}

private:
	std::unique_ptr<TaskQueue<T>> m_TaskQueue; // 任务队列
	std::vector<std::thread> m_threads;       // 线程id数组
	std::thread m_manager;                    // 管理者线程

	// 线程池状态信息
	int m_minNum;  // 线程池中能容纳的最小的线程数量
	int m_maxNum;  // 线程池中能容纳的最大的线程数量

	int m_busyNum; // 正在工作的线程数量
	int m_aliveNum; // 活着的线程数量

	int m_toBeExitNum; // 需要结束的线程数量

	bool m_shutdown;   // 标记线程池是否已经销毁

	int m_checkSec;

	std::condition_variable m_cond; // 条件变量,负责协调,提示有新的任务或者阻塞没有任务可做的线程
	std::mutex m_mutex;             // 锁对象
};

5、测试

#include <iostream>
using namespace std;
#include <string>
#include <thread>

#include "ThreadPool.h"

class Task_
{
public:
	void operator()(initializer_list<string> arg)
	{
		this_thread::sleep_for(chrono::seconds(2));
		for (auto it = arg.begin(); it != arg.end(); it++)
		{
			cout << *it << "\t";
		}
		cout << endl;
	}
};
void test_task()
{
	initializer_list<string> lis1 = { "java", "c" };
	Task<initializer_list<string>> task1(Task_(), lis1);

	initializer_list<string> lis2 = { "python", "c" };
	Task<initializer_list<string>> task2(Task_(), lis2);

	initializer_list<string> lis3 = { "golang", "c" };
	Task<initializer_list<string>> task3(Task_(), lis3);

	/*TaskQueue<initializer_list<string>> taskQueue;
	taskQueue.addTask(task1);
	taskQueue.addTask(task2);
	taskQueue.addTask(task3);*/

	ThreadPool<initializer_list<string>> pool(3, 6);
	pool.addTask(task1);
	pool.addTask(task2);
	pool.addTask(task3);


	this_thread::sleep_for(chrono::milliseconds(300));

	int i = 0;
	while (true)
	{
		cout << "正在忙碌中的线程数量 -> " << pool.getBusyNum() << endl;
		cout << "存活状态中的线程数量 -> " << pool.getAliveNum() << endl;
		cout << "当前任务数 -> " << pool.getTaskNum() << endl;
		this_thread::sleep_for(chrono::seconds(3));

		for (int i = 0; i < 6; i++)
		{
			pool.addTask(task3);
		}
		i++;
		if (i == 8)
		{
			break;
		}
	}
}

int main(int argc, char * argv[])
{
	test_task();

	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VVPU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值