在多线程编程中,线程池是一种非常实用的技术,它可以有效地管理线程的生命周期,避免频繁创建和销毁线程带来的性能开销。本文将详细介绍一个基于 C++ 实现的线程池,包括其实现原理、代码结构以及如何使用该线程池来执行任务。
代码整体结构概述
我们的线程池代码主要由两个类组成:Thread
类和 ThreadPool
类。Thread
类代表一个线程,封装了线程的创建和启动操作;ThreadPool
类则是线程池的核心,负责管理线程的生命周期、任务队列的维护以及任务的分配和执行。
代码详细分析
1. 头文件和常量定义
cpp
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>
#include <thread>
#include <map>
#include <future>
const int TASK_MAX_THRESHHOLD = 1024;
const int THREAD_MAX_THRESHHOLD = 50;
const int THREAD_MAX_IDLE_TIME = 10; // 单位 s
- 引入了一系列必要的头文件,用于实现线程池所需的各种功能,如多线程同步、内存管理、任务封装等。
- 定义了三个常量:
TASK_MAX_THRESHHOLD
:任务队列的最大容量,当任务队列中的任务数量达到该值时,新的任务提交可能会失败。THREAD_MAX_THRESHHOLD
:线程池中的最大线程数量,用于限制线程池的并发程度。THREAD_MAX_IDLE_TIME
:线程的最大空闲时间,当线程空闲时间超过该值时,可能会被销毁以节省资源。
2. 线程池模式枚举
enum PoolMode//两种模式
{
MODE_FIXED, // 静态
MODE_CACHED // 动态
};
定义了线程池的两种工作模式:
MODE_FIXED
:静态模式,线程池中的线程数量固定,在创建线程池时指定初始线程数量,后续不会动态调整。MODE_CACHED
:动态模式,线程池会根据任务的数量动态调整线程数量,当任务较多时会创建新的线程,当线程空闲时间过长时会销毁线程。
3. Thread
类
class Thread
{
public:
using ThreadFunc = std::function<void(int)>;
void start()
{
// 创建线程执行
std::thread t(func_, threadId_);
t.detach(); // 设置分离线程
}
Thread(ThreadFunc func) :
func_(func),
threadId_(generateId_++)
{}
~Thread()
{
std::cout << std::this_thread::get_id() << " " << " Thread" << std::endl;
}
int getId()
{
return threadId_;
}
void setThreadMode(bool isInitThread)
{
isInitThread_ = isInitThread;
}
bool getThreadMode()
{
return isInitThread_;
}
private:
ThreadFunc func_;
std::thread thread_;
static int generateId_;
int threadId_; // 保存线程 id
bool isInitThread_;
};
int Thread::generateId_ = 0;
Thread
类代表一个线程,其中:ThreadFunc
:定义了线程执行的函数类型,是一个接受int
类型参数并返回void
的函数对象。start()
方法:创建一个新的线程,并将func_
作为线程的执行函数,同时调用detach()
方法将线程设置为分离状态,使其在后台独立运行。- 构造函数:接受一个
ThreadFunc
类型的参数,用于初始化线程的执行函数,并为线程分配一个唯一的threadId_
。 getId()
方法:返回线程的唯一标识符。setThreadMode()
和getThreadMode()
方法:用于设置和获取线程的模式,区分该线程是初始创建的线程还是后续动态创建的线程。
4. ThreadPool
类
构造函数和析构函数
class ThreadPool
{
public:
ThreadPool()
: initThreadSize_(0),
taskSize(0),
taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD),
poolMode_(PoolMode::MODE_FIXED),
isPoolrunning(false),
idleThreadSize_(0),
threadSizeThreshHold_(THREAD_MAX_THRESHHOLD),
curThreadSize_(0),
stop(false)
{}
~ThreadPool()
{
isPoolrunning = false;
std::unique_lock<std::mutex> lock(taskQueMtx_);
notEmpty_.notify_all();
exitCond_.wait(lock, [&]()->bool { return threads_.size() == 0; });
}
- 构造函数:初始化线程池的各种参数,包括初始线程数量、任务队列最大容量、线程池模式等。
- 析构函数:将线程池的运行状态设置为
false
,通知所有等待任务的线程,然后等待所有线程退出,确保线程资源被正确释放。
线程池启动和配置方法
void start(int initThreadSize)
{
isPoolrunning = true;
initThreadSize_ = initThreadSize; // 初始线程数量
curThreadSize_ = initThreadSize;
// 创建线程
for (int i = 0; i < initThreadSize_; i++)
{
addThread(true);
}
}
void setTaskQueMaxThreshHold(int threshold)
{
if (checkRunningState()) return;
taskQueMaxThreshHold_ = threshold;
}
void setThreadSizeThreshHold(int threshhold)
{
if (checkRunningState()) return;
if (poolMode_ == PoolMode::MODE_CACHED)
{
threadSizeThreshHold_ = threshhold;
}
}
void setMode(PoolMode mode)
{
if (checkRunningState()) return;
poolMode_ = mode;
}
start(int initThreadSize)
方法:启动线程池,将线程池的运行状态设置为true
,并根据传入的初始线程数量创建相应数量的线程。setTaskQueMaxThreshHold(int threshold)
方法:设置任务队列的最大容量,只有在线程池未运行时才能修改。setThreadSizeThreshHold(int threshhold)
方法:设置线程池的最大线程数量,仅在动态模式下有效,且只有在线程池未运行时才能修改。setMode(PoolMode mode)
方法:设置线程池的工作模式,只有在线程池未运行时才能修改。
任务提交方法
template <typename Func, typename... Args>
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> ul(taskQueMtx_);
// 线程通信 等空余
if (!notFull_.wait_for(ul, std::chrono::seconds(1), [this]() -> bool {
return this->taskQue_.size() < (size_t)this->taskQueMaxThreshHold_;
})) {
// 等待一秒未满足
std::cerr << "task queue is full,submit task failed." << std::endl;
auto newTask = std::make_shared<std::packaged_task<RType()>>(
[]() -> RType { return RType(); }
);
return newTask->get_future();
}
// 空余就放入队列中
taskQue_.emplace([task]() {
(*task)();
});
taskSize++;
// cached模式检查是否要开新的线程,小而快的任务
std::cout << "Task queue size: " << taskQue_.size() << ", Idle thread size: " << idleThreadSize_ << std::endl;
// 在notEmpty_上通知
notEmpty_.notify_all();
if (poolMode_ == PoolMode::MODE_CACHED
&& taskQue_.size() > idleThreadSize_
&& curThreadSize_ < threadSizeThreshHold_
) {
addThread(false);
// 修改线程个数相关变量
curThreadSize_++;
idleThreadSize_++;
}
return result;
}
submitTask
是一个模板函数,用于向线程池提交任务。它接受一个可调用对象func
及其参数args
,并返回一个std::future
对象,用于获取任务的执行结果。- 首先,使用
std::make_shared
创建一个std::packaged_task
对象,将func
和args
绑定在一起,并通过get_future()
方法获取与该任务关联的std::future
对象。 - 然后,获取任务队列的锁,检查任务队列是否已满。如果队列已满,等待 1 秒钟,如果仍然满则输出错误信息,并创建一个返回默认值的任务,返回其关联的
std::future
对象。 - 如果队列有空余,将任务封装成一个 lambda 表达式并放入任务队列中,同时增加任务数量。
- 最后,通知所有等待任务的线程,并根据线程池的模式和任务队列的情况,决定是否需要创建新的线程。
- 首先,使用
线程执行函数
void threadFunc(int threadid)
{
auto lastTime = std::chrono::high_resolution_clock().now();
for (;;)
{
Task task;
// 获取锁
std::cout << "tid:" << std::this_thread::get_id()
<< "尝试获取任务..." << std::endl;
{
std::unique_lock<std::mutex> ul(taskQueMtx_);
// 等通信
while (taskQue_.size() == 0)
{
if (!isPoolrunning)
{
std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
exitCond_.notify_all();
return;
}
if (poolMode_ == PoolMode::MODE_CACHED) {
if (std::cv_status::timeout == notEmpty_.wait_for(ul, 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_)
{
if (threads_[threadid]->getThreadMode() == false) { // 判断这个线程是不是后续新创建的
std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
}
return;
}
}
}
else
{
notEmpty_.wait(ul);
}
}
// 取任务
std::cout << "tid:" << std::this_thread::get_id()
<< "获取任务成功..." << std::endl;
idleThreadSize_--;
task = taskQue_.front();
taskQue_.pop();
taskSize--;
}
if (!taskQue_.empty())
{
notEmpty_.notify_all();
}
notFull_.notify_all();
// 跑任务
if (!task) {
continue;
}
task();
lastTime = std::chrono::high_resolution_clock().now();
idleThreadSize_++;
}
}
threadFunc
是线程的执行函数,每个线程都会执行该函数。- 线程进入一个无限循环,不断尝试从任务队列中获取任务。
- 在获取任务之前,需要获取任务队列的锁,并检查队列是否为空。如果队列为空,根据线程池的模式和线程的空闲时间,决定是否等待任务或退出线程。
- 如果队列中有任务,取出任务并执行,执行完毕后更新线程的空闲时间和相关状态。
新增线程方法
cpp
void addThread(bool isInitial)
{
auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
int threadId = ptr->getId();
if (isInitial) {
std::cout << "creat init Thread :" << threadId << std::endl;
ptr->setThreadMode(true);
}
else
{
std::cout << "creat new success:" << threadId << std::endl;
ptr->setThreadMode(false);
}
threads_.emplace(threadId, std::move(ptr));
threads_[threadId]->start();
idleThreadSize_++;
curThreadSize_++;
}
addThread
方法用于创建新的线程。- 使用
std::make_unique
创建一个Thread
对象,并将threadFunc
绑定为线程的执行函数。 - 根据线程是否为初始创建的线程,设置线程的模式。
- 将线程对象添加到线程池中,并启动线程。
- 使用
5. 主函数
cpp
int add(int a, int b)
{
std::cout << "A+B" << std::endl;
return a + b;
}
int main()
{
ThreadPool pool;
pool.setMode(MODE_CACHED);
pool.start(1);
std::future<int> fu = pool.submitTask(add, 1, 3);
std::future<int> fu2 = pool.submitTask(add, 1, 3);
std::future<int> fu3 = pool.submitTask(add, 1, 3);
std::future<int> fu4 = pool.submitTask(add, 1, 3);
std::cout << fu.get() << std::endl;
return 0;
}
- 定义了一个简单的函数
add
,用于演示任务的执行。 - 在
main
函数中,创建一个线程池对象,设置为动态模式,并启动线程池,初始线程数量为 1。 - 向线程池提交 4 个任务,每个任务都是调用
add
函数,并将结果存储在std::future
对象中。 - 最后,通过
fu.get()
获取第一个任务的执行结果并输出。
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>
#include <thread>
#include <map>
#include <future>
#include <chrono>
const int TASK_MAX_THRESHHOLD = 1024;
const int THREAD_MAX_THRESHHOLD = 50;
const int THREAD_MAX_IDLE_TIME = 10;//单位 s
enum PoolMode//两种模式
{
MODE_FIXED,//静态
MODE_CACHED,//动态
};
//线程池类型
class Thread
{
public:
using ThreadFunc = std::function<void(int)>;
void start()
{
//创建线程执行
std::thread t(func_, threadId_);
t.detach();//设置分离线程
}
Thread(ThreadFunc func) :
func_(func)
, threadId_(generateId_++)
{}
~Thread()
{
std::cout << std::this_thread::get_id() << " " << " Thread" << std::endl;
}
int getId()
{
return threadId_;
}
void setThreadMode(bool isInitThread)
{
isInitThread_ = isInitThread;
}
bool getThreadMode()
{
return isInitThread_;
}
private:
ThreadFunc func_;
std::thread thread_;
static int generateId_;
int threadId_;//保存线程id
bool isInitThread_;
};
int Thread::generateId_ = 0;
//线程池类
class ThreadPool
{
public:
ThreadPool()
:initThreadSize_(0)
, taskSize(0)
, taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
, poolMode_(PoolMode::MODE_FIXED)
, isPoolrunning(false)
, idleThreadSize_(0)
, threadSizeThreshHold_(THREAD_MAX_THRESHHOLD)
, curThreadSize_(0)
, stop(false)
{}
~ThreadPool()
{
isPoolrunning = false;
std::unique_lock<std::mutex>lock(taskQueMtx_);
notEmpty_.notify_all();
exitCond_.wait(lock, [&]()->bool {return threads_.size() == 0; });
}
void start(int initThreadSize)
{
isPoolrunning = true;
initThreadSize_ = initThreadSize;//初始线程数量
curThreadSize_ = initThreadSize;
//创建线程
// creatThreadId_=std::this_thread::get_id();
for (int i = 0; i < initThreadSize_; i++)
{
addThread(true);
}
// 启动
}
void setTaskQueMaxThreshHold(int threshold)
{
if (checkRunningState())return;
taskQueMaxThreshHold_ = threshold;
}
void setThreadSizeThreshHold(int threshhold)
{
if (checkRunningState())return;
if (poolMode_ == PoolMode::MODE_CACHED)
{
threadSizeThreshHold_ = threshhold;
}
}
void setMode(PoolMode mode)
{
if (checkRunningState())return;
poolMode_ = mode;
}
//Result submitTask(std::shared_ptr<Task>sp);
template <typename Func, typename... Args>
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> ul(taskQueMtx_);
// 线程通信 等空余
if (!notFull_.wait_for(ul, std::chrono::seconds(1), [this]() -> bool {
return this->taskQue_.size() < (size_t)this->taskQueMaxThreshHold_;
})) {
// 等待一秒未满足
std::cerr << "task queue is full,submit task failed." << std::endl;
auto newTask = std::make_shared<std::packaged_task<RType()>>(
[]() -> RType { return RType(); }
);
return newTask->get_future();
}
// 空余就放入队列中
taskQue_.emplace([task]() {//只能捕获值因为引用计数要加一,如果不加函数没了就销毁了!!
(*task)();
});
taskSize++;
// cached模式检查是否要开新的线程,小而快的任务
std::cout << "Task queue size: " << taskQue_.size() << ", Idle thread size: " << idleThreadSize_ << std::endl;
// 在notEmpty_上通知
notEmpty_.notify_all();
if (poolMode_ == PoolMode::MODE_CACHED
&& taskQue_.size() > idleThreadSize_
&& curThreadSize_ < threadSizeThreshHold_
) {
addThread(false);
// 修改线程个数相关变量
curThreadSize_++;
idleThreadSize_++;
}
return result;
}
ThreadPool(const ThreadPool&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
void threadFunc(int threadid)
{
/* std::cout << "begin threadFunc" << std::endl;
std::cout << std::this_thread::get_id() << std:: endl;*/
//获取锁
auto lastTime = std::chrono::high_resolution_clock().now();
for (;;)
{
Task task;
//获取锁
std::cout << "tid:" << std::this_thread::get_id()
<< "尝试获取任务..." << std::endl;
{//没必要一直拿着锁,取出来任务后就可以释放锁了
std::unique_lock<std::mutex> ul(taskQueMtx_);
//等通信
//因为超时返回的线程
while (taskQue_.size() == 0)
{
if (!isPoolrunning)
{
std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
exitCond_.notify_all();
return;
}
if (poolMode_ == PoolMode::MODE_CACHED) {
if (std::cv_status::timeout == notEmpty_.wait_for(ul, 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_)
{
if (threads_[threadid]->getThreadMode() == true) {//判断这个线程是不是后续新创建的
std::cout << "threadid:" << std::this_thread::get_id() << " exit thread" << std::endl;
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
}
return;
}
}
}
else
{
notEmpty_.wait(ul);
}
}
//取任务
std::cout << "tid:" << std::this_thread::get_id()
<< "获取任务成功..." << std::endl;
idleThreadSize_--;
task = taskQue_.front();
taskQue_.pop();
taskSize--;
}
if (!taskQue_.empty())
{
notEmpty_.notify_all();
}
notFull_.notify_all();
//跑任务
if (task != nullptr)
{
//task->run();
task();
}
lastTime = std::chrono::high_resolution_clock().now();
idleThreadSize_++;
}
}
void addThread(bool isInitial)
{
auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
int threadId = ptr->getId();
if (isInitial) {
std::cout << "creat init Thread :" << threadId << std::endl;
ptr->setThreadMode(true);
}
else
{
std::cout << "creat new success:" << threadId << std::endl;
ptr->setThreadMode(false);
}
threads_.emplace(threadId, std::move(ptr));
threads_[threadId]->start();
idleThreadSize_++;
curThreadSize_++;
}
bool checkRunningState()
{
return isPoolrunning;
}
void changeStop()
{
stop = !stop;
}
/*
example:
ThreadPool pool;
pool.start(4);
pool.submitTask(std::make_shared(MyTask());
*/
private:
// std::vector<std::unique_ptr<Thread>>threads_;//线程列表
std::unordered_map<int, std::unique_ptr<Thread>>threads_;
std::atomic_int curThreadSize_;//当前线程数量
int threadSizeThreshHold_;//线程上限
size_t initThreadSize_;//初始线程数量
std::atomic_int idleThreadSize_;//记录空闲线程数量
//std::thread::id creatThreadId_;//start里创建的线程id
using Task = std::function<void()>;
std::queue<Task>taskQue_;//任务队列
std::atomic_int taskSize;//任务数量
int taskQueMaxThreshHold_;//任务上限
std::mutex taskQueMtx_;
std::condition_variable notFull_;//队列不满
std::condition_variable notEmpty_;//队列不空
std::condition_variable exitCond_;//等待线程资源回收
PoolMode poolMode_;//当前线程池工作模式
std::atomic_bool isPoolrunning;//表示是否已经开始
bool stop;
};
#endif
int add(int a, int b)
{
std::cout << "A+B" << std::endl;
return a + b;
}
int main()
{
ThreadPool pool;
pool.setMode(MODE_CACHED);
pool.start(1);
std::future<int>fu = pool.submitTask(add, 1, 3);
std::future<int>fu2 = pool.submitTask(add, 1, 3);
std::future<int>fu3 = pool.submitTask(add, 1, 3);
std::future<int>fu4 = pool.submitTask(add, 1, 3);
std::cout << fu.get() << std::endl;
return 0;
}