线程池的概念
为什么要引进线程池
我们原本用多线程,可以把任务丢进线程里面去,让他执行。假如这个任务执行时间很长,而且任务量很少。一个任务创建一个线程并没有什么关系。但是,如果处理的任务用时很短,且量大。由于线程创建销毁都得时间,这部分会占用了太多运行的时间,影响性能,所有需要做一些东西来改善。
大致做法
比如说,先创建5个线程(举个例子),然后让所有的线程都阻塞,添加一个任务,就往线程池里面丢任务,让里面的线程来处理这个任务,处理完又接着阻塞。这样省去了线程创建和销毁的时间。
生活中的例子
好比,你去超市买东西,要结账。普通的便利店,就一个人在收银,这就是传统的单进程,单线程。让你等得很不舒服对吧。你去了一家大型的超市,有五个柜台(一开始创建了五个线程,进入等待模式),这时候跟你一起来的有6个人(有六个任务加入),这时候五个柜台都没有人(进入阻塞状态)。然后第一个人去了一号柜台,第二个人发现一号柜台有人了,就去了二号柜台。。。。最后到了第六个人,发现前五个都有人了,他就开始排队,一发现这5个柜台有一个是空的,就冲过去算账。这就是我理解的线程池。这也许有点出入,把它换成医院叫号会更贴切。
预备知识
由于这是在linux下用c/c++写的线程池
1.C/C++
2.Linux的socket和pthread,还有pthread_mutex_t ,pthread_cond_t(不会的搜索这几个关键词,看一下就会懂了)
3.C++vector容器(看一下也就懂了)
4.Makefile(这个关系不大)
代码部分
一共有三个类:
1.NThread :封装一个线程类
2.Task : 用来存放Task任务的类
3.ThreadPoll : 线程池
NThread
传入一个线程的运行函数,和void * 参数
调用start函数开始运行线程
nthread.h
#ifndef _NTHREAD_H_
#define _NTHREAD_H_
#include <pthread.h>
class NThread
{
public:
typedef void *(threadFun_t)(void *arg);
NThread(threadFun_t * Fun,void *arg);
~NThread();
void start(); //线程启动
void join(); //等待线程结束
static void * threadRun(void * arg); //线程执行函数
pthread_t getThreadId(); //获取线程id
bool getisStart(); //获取是否运行状态
private:
bool isStart; //判断线程是否执行
threadFun_t * Fun; //user的线程函数
void * argv; //arg参数
pthread_t pthreadId; //线程id
};
#endif //_NTHREAD_H_
nthread.cpp
#include "nthread.h"
NThread::NThread(threadFun_t * Fun,void *arg)
{
this->argv = arg;
this->Fun = Fun;
pthread_create(&this->pthreadId,NULL,this->threadRun,this);
}
NThread::~NThread()
{
}
bool NThread::getisStart()
{
return this->isStart;
}
void * NThread::threadRun(void * arg)
{
NThread * thread = (NThread *)arg;
while(!thread->isStart);
thread->Fun(thread->argv);
}
void NThread::start()
{
this->isStart = true;
}
pthread_t NThread::getThreadId()
{
return this->pthreadId;
}
void NThread::join()
{
pthread_join(this->pthreadId,NULL);
}
Task
这是一个抽象类,里面必须要有一个run函数,至于run函数的参数可以,可以继承Task,然后放在类里面
task.h
#ifndef _TASK_H_
#define _TASK_H_
#include <stdio.h>
#include <unistd.h>
class Task
{
public:
Task();
~Task();
virtual void run() = 0;
};
#endif //_TASK_H_
task.cpp
#include "task.h"
Task::Task()
{
}
Task::~Task()
{
}
nthreadpoll
创建线程池有两个参数
1.线程最大数量
2.一开始创建线程的数量
调用AddTask就能够添加任务了
nthread_poll.h
#ifndef _THREAD_POLL_H_
#define _THREAD_POLL_H_
#include <iostream>
#include <pthread.h>
#include <vector>
#include "task.h"
#include "nthread.h"
using namespace std;
/*
* 构造函数有两个参数,(最大线程,每次新创建的线程)
* 当任务容器里面的任务是 可用的两倍就新增 $(每次新创建的线程) 的数量
*/
class ThreadPoll
{
private:
int ThreadNum; //当前线程数量
int MaxThread; //最大线程数量
int CreateThread; //每次创建的大小
int ActiveThread; //当前活跃的线程数量
pthread_mutex_t mutex;
pthread_mutex_t mutexActive;
pthread_cond_t cond;
vector<Task *> TaskList; //任务向量列表
pthread_t * phtread_id; //线程id
vector<NThread *> NThreadList; //NThread 对象向量列表
bool isRun; //是否运行的标志位
private:
static void * threadFunc(void * p); //线程函数
public:
enum ThreadCtrl{ADD,DEL}; //添加或者减少计数
ThreadPoll(int maxThread,int createThread = 4); //构造函数 maxThread为最大的线程createThread为每次最多创建的线程
~ThreadPoll(); //析构
void AddTask(Task * Addtask); //添加任务到任务向量列表
void Destroy(); //结束所有线程,正在运行的等待运行
void ChangeActiveThread(ThreadCtrl ctrl); //改变当前占用进程的累计计数
int GetSleepThreadNum(); //获得可用的进程数量
};
#endif //_THREAD_POLL_H_
nthread_poll.cpp
#include "thread_poll.h"
ThreadPoll::ThreadPoll(int maxThread,int createThread)
{
this->ThreadNum = createThread;
this->CreateThread = createThread;
this->MaxThread = maxThread;
this->ActiveThread = 0;
this->isRun = true;
if(maxThread < createThread)
{
this->MaxThread = this->ThreadNum;
}
pthread_mutex_init(&mutex,NULL);
pthread_mutex_init(&mutexActive,NULL);
pthread_cond_init(&cond,NULL);
this->phtread_id = new pthread_t[maxThread];
for(int i = 0 ;i < ThreadNum;i++)
{
//pthread_create(&phtread_id[i],NULL,threadFunc,this);
NThread * nthreadCreate = new NThread(threadFunc,this);
NThreadList.push_back(nthreadCreate);
phtread_id[i] = nthreadCreate->getThreadId();
nthreadCreate->start();
cout<<"pthread "<<i<<" id : "<<phtread_id[i]<<endl;
}
}
ThreadPoll::~ThreadPoll()
{
delete phtread_id;
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&mutexActive);
pthread_cond_destroy(&cond);
// for(auto it = NThreadList.begin();it !=NThreadList.end();it++)
// {
// delete *it;
// NThreadList.erase(it);
// }
NThreadList.clear();
}
void * ThreadPoll::threadFunc(void * p)
{
ThreadPoll *poll = (ThreadPoll *)p;
while (1)
{
//判断线程退出
if(!poll->isRun)
{
break;
}
pthread_mutex_lock(&poll->mutex);
//如果任务列表为空,就进入等待队列
while(poll->TaskList.size() == 0)
{
pthread_cond_wait(&poll->cond,&poll->mutex);
}
poll->ChangeActiveThread(ADD);
auto it = poll->TaskList.begin();
Task * getTask = *it;
poll->TaskList.erase(it);
pthread_mutex_unlock(&poll->mutex);
cout<<"pthread task run ,thread id = "<<pthread_self()<<endl;
getTask->run();
poll->ChangeActiveThread(DEL);
}
}
void ThreadPoll::AddTask(Task * Addtask)
{
pthread_mutex_lock(&mutex);
//如果任务数大于最大线程的2倍自动丢弃
if(TaskList.size() <= 2*MaxThread)
{
TaskList.push_back(Addtask);
pthread_cond_signal(&cond);
}
else
{
printf("任务数量太大,舍弃\n");
}
//存在任务为可用的2倍就创建 createThread数量的线程
if((this->MaxThread != this->TaskList.size()) &&(this->TaskList.size() >= 2*this->GetSleepThreadNum()))
{
int create = 0;
if(this->ThreadNum + this->CreateThread > this->MaxThread)
{
create = this->MaxThread-this->ThreadNum;
}
else
{
create = this->CreateThread;
}
for(int i = 0 ;i < create;i++)
{
//pthread_create(&phtread_id[this->ThreadNum],NULL,threadFunc,this);
NThread * nthreadCreate = new NThread(threadFunc,this);
NThreadList.push_back(nthreadCreate);
phtread_id[this->ThreadNum] = nthreadCreate->getThreadId();
nthreadCreate->start();
cout<<"pthread "<<i+this->ThreadNum<<" id : "<<phtread_id[this->ThreadNum]<<endl;
}
this->ThreadNum += create;
}
pthread_mutex_unlock(&mutex);
}
void ThreadPoll::Destroy()
{
this->isRun = false;
}
void ThreadPoll::ChangeActiveThread(ThreadCtrl ctrl)
{
if(ctrl == ADD)
{
pthread_mutex_lock(&mutexActive);
ActiveThread++;
pthread_mutex_unlock(&mutexActive);
}
else if(ctrl == DEL)
{
pthread_mutex_lock(&mutexActive);
ActiveThread--;
pthread_mutex_unlock(&mutexActive);
}
}
int ThreadPoll::GetSleepThreadNum()
{
return this->ThreadNum - this->ActiveThread;
}