原理
如我们每次动态开辟一个变量空间的时候,都会有效率的损失,如果有一个内存池,需要的化直接在里面取就行了,这就大大的提高了效率
当我们创建任务的时候再去申请线程,就相当于我们需要malloc的时候再去申请空间,创建线程也是有成本的,
请求来了,线程要提前准备好,任务来了,就指派给他
提前准备好的线程,原来随时处理任务就叫做线程池
(提高效率)
实现
Task.hpp
使用这个Task类,将此作为里面的类型
Task.hpp
#pragma once
#include <iostream>
#include <pthread.h>
namespace ns_task
//使用命名空间,提高代码的安全性
{
class Task
{
private:
int _x;
int _y;
char _op; //表示+-*/%
public:
Task() //无参构造,为了拿任务,不需要参数列表
{
}
//进行函数重载
Task(int x, int y, char op) //有参构造,制造任务
: _x(x), _y(y), _op(op)
{ }
~Task()//析构函数其实也不需要我们来实现,因为没有变量需要销毁
{
}
int Run()//task类,处理任务
{
int res = 0;
switch (_op)
{
case '+':
res = _x + _y;
break;
case '-':
res = _x - _y;
break;
case '*':
res = _x * _y;
break;
case '/':
res = _x / _y;
break;
case '%':
res = _x % _y;
break;
default:
std::cout << "bug?" << std::endl;
break;
}
std::cout << "当前任务正在被:" << pthread_self() << "处理:" << _x << _op << _y << "=" << res << std::endl;
return res;
}
Task operator=(Task &s)//赋值运算符重载
{
if (this != &s)
{
_x = s._x;
_y = s._y;
_op = s._op;
}
return *this;
}
//Task t;
//t()//
int operator()()//将()重载,仿函数
{
return Run();//()使用run()
}
};
}
threadpool.hpp
实现线程池类,高封装性,将所有的对于线程的操作都放在这个类里面是实现
#pragma once
#include <iostream>
#include <string>
#include <queue>
#include<unistd.h>
#include<pthread.h>
// #cludine"Task.hpp"
// using namespace ns_task;
namespace ns_threadpool
{
const int g_num=5;//使用一个全局变量,作为我们需要创建的线程的个数
template <class T>//使用模板,可以实现泛型编程,多个类型都可以使用
class ThreadPool //线程池
{
private:
int num_; //一个线程池里面有多少个任务
std::queue<T> task_queue_; //任务队列,临界资源
pthread_mutex_t mtx_;//锁
pthread_cond_t cond_;//条件变量
public:
ThreadPool(int num=g_num) :num_(num)//初始化构造函数
{
pthread_mutex_init(&mtx_,nullptr);
pthread_cond_init(&cond_,nullptr);
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx_);
pthread_cond_destroy(&cond_);
}
//在类中,要让
static void* Rountine(void* args)
//主线程里面往里面塞任务,而线程里面主要就是在里面处理任务
//也不能访问类里面非static成员
{
pthread_detach(pthread_self());//实现线程分离就不要再去join等待了
ThreadPool<T>* tp=(ThreadPool<T>*)args;//类型转换
while(true)
{
//从任务队列里面去拿一个任务
//执行任务,要先把这个任务队列锁主
//每个线程他跟放任务的线程一样,都是竞争式的去拿一个任务
tp->Lock();
//先检测任务队列是否有一个任务
while(tp->IsEmpty())
{
//检测到任务队列为空
//此时线程就挂起等待
tp->Wait();
}
//该任务队列里面一定有任务了
T t;
tp->PopTask(&t);
//任务就拿到了
tp->UnLock();
t.Run();//可能有多个线程在处理任务,
sleep(1);
}
}
//我们定义了一个线程池的变量,就需要首先先进行初始化
void InitThreadPool()
{
//初始化一批线程,
//这样就不要每次用都要去开辟线程了
pthread_t tid;//一次创建一批线程
for(int i=0;i<num_;i++)
{
pthread_create(&tid,nullptr,Rountine,(void*)this);//在create里面实现函数,所以我们需要在类里面实现这个函数
//在类中不能执行线程的方法,因为他都有隐藏的this指针
//所以我们需要使用静态的函数,就没有了this指针
}
}
void PopTask(T* out)//头删,在线程池里面取出任务
{
*out=task_queue_.front();
task_queue_.pop();
}
void Wait()
{
pthread_cond_wait(&cond_,&mtx_);
}
bool IsEmpty()
{
return task_queue_.empty();
}
void Lock()
{
pthread_mutex_lock(&mtx_);
}
void UnLock()
{
pthread_mutex_unlock(&mtx_);
}
void Wakeup()
{
pthread_cond_signal(&cond_);
}
//我们定义了一个变量之后,初始化之后,就要向里面放任务,然后里面的线程,通过里面的队列,争抢任务
void PushTask(const T & in)
{
//塞任务,就相当于一个生产者,生产者之间要进行互斥访问
Lock();
task_queue_.push(in);
UnLock();
Wakeup();//塞完任务之后,就要唤醒里面的队列,来取任务
}
//万一任务队列里面一个任务都没有的话,那么线程池里面的每一个线程就要处于休眠状态,挂起等待
};
}
main.cc
非单例模式
#include"threadpool.hpp"
#include"Task.hpp"
#include<unistd.h>
#include<cstdlib>
#include<ctime>
using namespace ns_task;//使用我们写的两个命名空间,提高了代码的封装性
using namespace ns_threadpool;
//线程池就是一个单例模式,只有一个线程池就够了
int main()
{
ThreadPool<Task>* tp=new ThreadPool<Task>();//我们一开始就创建一个线程池类型,线程池类型我们只需要一个就行了,
tp->InitThreadPool();
//我们希望是主线程不断的向线程池里面push任务,线程池里面竞争任务,处理这些任务
//外部可能存在一个或者多个线程向里面塞任务
srand((long long)time(nullptr));
while(true)
{
//以后就是从网络里面来
//主线程就是把任务放到线程池里面去
//有的时候访问网站,挂掉了,OS受不了了,杀掉
Task t(rand()%20+1,rand()%10+1,"+-*/%"[rand()%5]);
tp->PushTask(t);//放进去让他进行处理工作
}
return 0;
}
详细代码可以查看
ThreadPool