一.先前代码及实现(本章需要应用)
基于linux操作系统的线程封装(可实现任意传递任意类型任意个数的参数)-CSDN博客
基于Linux操作系统的生产消费者队列封装(C++)-CSDN博客
二.线程池
线程池(ThreadPool)是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池中的线程数量通常是预先设定的,当任务数量超过线程数量时,任务会在队列中等待,直到线程变得可用。线程池的主要优点包括:
- 资源复用:通过复用已存在的线程而不是每次需要执行任务时都创建一个新线程,可以显著减少线程创建和销毁的开销。这对于需要频繁创建和销毁线程的应用程序来说,可以显著提高性能。
- 提高响应速度:当任务到达时,如果线程池中已经有可用的线程,那么任务可以立即开始执行,而不需要等待新线程的创建。这可以缩短任务的等待时间,提高应用程序的响应速度。
- 控制并发数量:通过限制线程池中的线程数量,可以有效地控制并发执行的线程数量,从而避免过多的线程同时运行导致系统资源耗尽。这对于需要限制并发访问共享资源(如数据库连接、文件句柄等)的应用程序来说非常重要。
- 管理线程生命周期:线程池可以自动管理线程的生命周期,包括线程的创建、启动、暂停、恢复和销毁等。这使得开发人员无需直接处理这些繁琐的线程管理工作,可以更加专注于实现业务逻辑。
- 提供统一的线程管理策略:线程池可以为应用程序提供统一的线程管理策略,如线程优先级、超时处理、异常处理等。这些策略可以帮助开发人员更好地控制线程的执行过程,提高应用程序的稳定性和可靠性。
- 便于监控和调优:通过监控线程池的状态(如线程数量、任务队列长度等),开发人员可以了解应用程序的负载情况,并据此进行调优。例如,当发现任务队列长度过长时,可以动态增加线程数量以提高处理速度;当系统资源紧张时,可以减少线程数量以降低资源消耗。
三.线程池的封装实现
1.成员变量构造以及析构函数
static const size_t _thread_max_task=50;//任务队列最大数量
template<typename T1, typename T2=prodcon<T1>>/任务类型及存放任务的队列
//默认存放任务的队列为queue,可为priority_queue,具体参考,生产消费队列那一章
class threadpool
{
public:
typedef T1 task ; //任务
typedef T2 type_task; //存放任务的队列
using func=std::function<void(threadpool<task,type_task>*)>;
//回调函数,默认回调函数第一个参数为线程池指针
template<class Fn>
explicit threadpool(Fn func) //构造函数,只传递回调函数
:_mutex(nullptr),
_func([func](threadpool* _this){func(_this);})
//lmbda表达式绑定回调函数
,_prodcon(_thread_max_task)
//初始化生产消费者队列
{}
template<class Fn,class... Args>
explicit threadpool(Fn func,Args&...args)
//构造函数,传递回调函数,和其他可变参数(引用)
:_mutex(nullptr),
_func([func,&args...](threadpool* _this){func(_this,args...);})
, _prodcon(_thread_max_task)
{}
template<class Fn,class... Args>
explicit threadpool(Fn func, Args&&...args)
//构造函数,传递回调函数,和其他可变参数(右值引用)
_mutex(nullptr),
_func([func,args...](threadpool* _this){func(args...);})
, _prodcon(_thread_max_task)
{}
//构造函数主要是为了绑定回调函数
~threadpool()
{
for(auto &e:_threads) delete e;
}
//析构函数需要释放创建线程所开辟的空间
private:
size_t _max_thread_num; //最大线程数
size_t _thread_cur_num; //当前线程数
type_task _prodcon; //生产消费者队列
func _func;
std::vector<thread*> _threads;//线程
mutex _mutex;//锁
};
2.初始化函数
static const size_t _thread_init_num=50;//初始化线程数量
static const size_t _thread_max_num=50;//线程最大数量
static void handler(threadpool* thread_this)
//线程回调函数,配合thread_create 使用
{
thread_this->_func(thread_this); //执行绑定的回调函数
}
void init(size_t thread_init_num=_thread_init_num, size_t max_thread=_thread_max_num)
{
_max_thread_num=max_thread;
_thread_cur_num=thread_init_num;
for(int i=0;i<_thread_cur_num;++i)
{
thread* th=new thread(handler,this); //创建线程
th->detach(); //线程分离
_threads.push_back(th);
}
}
3. 放入取出任务
void push_task(task& in,int pri=10)
//此处的int pri=10表示优先值,对于普通队列queue可以忽略
//对于优先级队列来说,pri越低,越优先执行
{
_prodcon.push_task(in,pri); //取出任务
}
void pop_task(task& out)
{
{
lockguard<mutex> loc(_mutex); //加锁,防止其他线程也可以创建该线程
if(_prodcon.full()&&_thread_cur_num<_max_thread_num) //判断生产消费者队列是否满
{
for(int i=0;i<3&&_thread_cur_num+i<_max_thread_num;++i)
{
//如果满了且没有达到线程上限就创造新线程
thread* th=new thread(handler,this);
th->detach(); //线程分离
_threads.push_back(th);
++_thread_cur_num;
}
}
}
_prodcon.pop_task(out); //取任务
}
4. 完整代码
#pragma once
#include<iostream>
#include<vector>
#include<queue>
#include<string>
#include<cstring>
#include<unistd.h>
#include<functional>
#include"lock.hpp" //自己封装的锁
#include"thread.hpp" //自己封装的线程
#include"prodcon.hpp" //自己封装的生产消费队列
static const size_t _thread_init_num=20; //初始化线程数量
static const size_t _thread_max_num=50; //线程最大数量
static const size_t _thread_max_task=50; //任务队列最大数量
namespace zwr //自定义命名空间
{
template<typename T1, typename T2=prodcon<T1>> //任务类型及存放任务的队列
//默认存放任务的队列为queue,可为priority_queue,具体参考,生产消费队列那一章
class threadpool
{
public:
typedef T1 task ; //任务
typedef T2 type_task; //存放任务的队列
using func=std::function<void(threadpool<task,type_task>*)>;
//回调函数,默认回调函数第一个参数为线程池指针
static void handler(threadpool* thread_this)
//线程回调函数,配合thread_create 使用
{
thread_this->_func(thread_this);
}
template<class Fn>
explicit threadpool(Fn func) //构造函数,只传递回调函数
:_mutex(nullptr),
_func([func](threadpool* _this){func(_this);})
,_prodcon(_thread_max_task)
{}
template<class Fn,class... Args>
explicit threadpool(Fn func,Args&...args)
//构造函数,传递回调函数,和其他可变参数(引用)
:_mutex(nullptr),
_func([func,&args...](threadpool* _this){func(_this,args...);})
, _prodcon(_thread_max_task)
{}
template<class Fn,class... Args>
explicit threadpool(Fn func, Args&&...args)
//构造函数,传递回调函数,和其他可变参数(右值引用)
:_mutex(nullptr),
_func([func,args...](threadpool* _this){func(args...);})
, _prodcon(_thread_max_task)
{}
void init(size_t thread_init_num=_thread_init_num, size_t max_thread=_thread_max_num)
{
_max_thread_num=max_thread;
_thread_cur_num=thread_init_num;
for(int i=0;i<_thread_cur_num;++i)
{
thread* th=new thread(handler,this); //创建线程,
th->detach(); //线程分离
_threads.push_back(th);
}
}
~threadpool()
{
for(auto &e:_threads) delete e;
}
void push_task(task& in,int pri=10)
//此处的int pri=10表示优先值,对于普通队列queue可以忽略
//对于优先级队列来说,pri越低,越优先执行
{
_prodcon.push_task(in,pri); //取出任务
}
void pop_task(task& out)
{
{
lockguard<mutex> loc(_mutex); //加锁,防止其他线程也可以创建该线程
if(_prodcon.full()&&_thread_cur_num<_max_thread_num) //判断生产消费者队列是否满
{
for(int i=0;i<3&&_thread_cur_num+i<_max_thread_num;++i)
{
//如果满了且没有达到线程上限就创造新线程
thread* th=new thread(handler,this);
_threads.push_back(th);
++_thread_cur_num;
}
}
}
_prodcon.pop_task(out); //取任务
}
private:
size_t _max_thread_num; //最大线程数
size_t _thread_cur_num; //当前线程数
type_task _prodcon; //生产消费队列
func _func;
std::vector<thread*> _threads;//线程
mutex _mutex;//锁
};
}
四.测试
1.测试代码
#include<iostream>
#include<unistd.h>
#include "threadpool.hpp"
using namespace zwr; //命名空间
struct print_task //一个打印任务
{
print_task(std::string& out)
:_out(out)
{}
print_task()
{}
~print_task()
{}
std::string _out;
};
void callback(threadpool<print_task>* _thpool,std::string& name)
{
print_task pt;
_thpool->pop_task(pt); //取出任务
printf("I am %s, I will print %s\n",name.c_str(),pt._out.c_str());
}
int main()
{
std::string name="print_task";
threadpool<print_task> thpool(callback,name);
thpool.init();
int i=0;
while(1)
{
std::string in="print"+std::to_string(i++);
print_task pt(in); //将任务放入线程池中
thpool.push_task(pt);
sleep(1);
}
return 0;
}
2.测试结果
I am print_task, I will print print0
I am print_task, I will print print1
I am print_task, I will print print2
I am print_task, I will print print3
I am print_task, I will print print4
I am print_task, I will print print5
I am print_task, I will print print6
I am print_task, I will print print7
I am print_task, I will print print8
I am print_task, I will print print9
I am print_task, I will print print10
I am print_task, I will print print11
I am print_task, I will print print12