1.线程池基本了解
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价
。线程池不仅能够保证内核的充分利用
,还能防止过分调度
。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
2.线程池的应用场景
1)需要大量的线程来完成任务,且完成任务的时间比较短。
WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2)对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3) 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.
3.实现一个简单的加法任务的线程池
1)首先我们创建一个任务类。
//回调函数
typedef int(*cal_t)(int,int);
class Task{
private:
int x;//操作数1
int y;//操作数2
int z;//运算结果
cal_t handler_task;//运算操作
public:
Task(int a,int b,cal_t handler_task_)//构造函数
:x(a),y(b),handler_task(handler_task_)
{
}
//执行操作
void Run()
{
z = handler_task(x,y);
}
//将结果输出
void Show()
{
cout<<"thread : "<<pthread_self()<<"Task finish,result is : "<<z<<endl;
}
~Task()
{
}
};
2)线程池类
《1》线程池的成员
class ThreadPool{
private:
queue<Task> Task_Queue;//将任务添加在一个队列中
bool IsStop;//线程池的工作状态
int ThreadNum;//线程池内的线程数量
//为了线程安全所以需要互斥锁与条件变量保护临界资源
pthread_mutex_t lock;
pthread_cond_t cond;
};
《2》根据测试实现线程池内的成员函数
#include"ThreadPool.hpp"
#define NUM 5 //设置线程池的线程数量
//任务操作
int Add(int x,int y)
{
return x + y;
}
int main()
{
//创建一个线程池类
ThreadPool *tp =new ThreadPool(NUM);
//线程池的初始化
tp -> InitThreadPool();
//为了便于理解,我们使用while循环先一直往任务队列添加任务。
int count =1;
while(1){
sleep(1);
Task t(count,count-1,Add);
tp->AddTask(t);
count++;
}
return 0;
}
a.线程池类的构造函数与析构函数
//构造函数初始化时,将IsStop状态设置为fasle,否则线程池无法工作。
ThreadPool(int num):ThreadNum(num),IsStop(false)
{}
b.线程池的初始化
void InitThreadPool()
{
//对互斥锁以及条件变脸初始化。
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond,NULL);
//设置变量控制线程的创建
int i = 0;
for(;i<ThreadNum;i++)
{
pthread_t tid;
pthread_create(&tid,NULL,route,(void *)this);
}
}
//析构函数完成互斥锁以及条件变量的销毁
~ThreadPool()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
c.初始化完后实现线程创建中的route函数
//C++中static函数内无this指针,C++类对线程操作要用static修饰
static void *route(void *arg)
{
ThreadPool *tp = (ThreadPool*)arg;
//将线程自己分离出来
pthread_detach(pthread_self());
while(1){
//为了保证线程安全,将任务队列加锁
tp->LockQueue();
//当任务队列为空时,线程先进入等待状态
if(tp->IsEmpty()){
tp->IdleThread();
}
//当队列有任务时,创建一个任务类并得到该任务,也由此得出需要一个AddTask();成员函数。
Task t = tp->GetTask();
//取到会解锁
tp->UnlockQueue();
//执行任务操作
t.Run();
//将任务结果显示
t.Show();
}
}
d.依次实LockQueue();IsEmpty();IDleThread();GetTask();UnlockQueue();
//任务队列加锁
void LockQueue()
{
pthread_mutex_lock(&lock);
}
//任务解锁解锁
void UnlockQueue()
{
pthread_mutex_unlock(&lock);
}
//判断任务是否为空
bool IsEmpty()
{
return Task_Queue.size() == 0 ? true : false;
}
//加任务
void AddTask(Task &t)
{
Task_Queue.push(t);
//当得到一个任务时便传送信号给一个进程
NoticOneThread();
UnlockQueue();
}
//传送信号给一个进程
void NoticOneThread()
{
pthread_cond_signal(&cond);
}
//等待队列
void IdleThread()
{
pthread_cond_wait(&cond,&lock);
}
//得到任务
Task GetTask()
{
Task t = Task_Queue.front();
Task_Queue.pop();
return t;
}
e.暂停分析
貌似我们已经实现了一个加法的简易线程池,首先创建一个线程池对象,然后进行初始化,创建设定数的线程,并且它们都处于等待状态,当有任务时,它们依次被唤醒,执行任务。但是我们测试用的是while(1)假如没有了任务,线程便一直处于了等待状态不会退出,于是我们还要实现一个Stop()成员函数,若停止了广播NoticAllThread()中的线程让他们依次退出,并修改IdleThread()以及AddTask()函数进行IsStop条件判断。
void Stop()
{
LockQueue();
IsStop = true;
UnlockQueue();
while(ThreadNum > 0){
NoticAllThread();
}
}
void IdleThread()
{
if(IsStop){
UnlockQueue();
ThreadNum--;
pthread_exit((void *)0);
cout<<"pthread "<<pthread_self() <<"quit"<<endl;
return ;
}
pthread_cond_wait(&cond,&lock);
}
void AddTask(Task &t)
{
if(IsStop)
{
UnlockQueue();
return ;
}
Task_Queue.push(t);
NoticOneThread();
UnlockQueue();
}
void NoticAllThread()
{
pthread_cond_broadcast(&cond);
}
3) 完整代码以及测试
头文件
#ifndef __THREADPOLL_HPP__
#define __THREADPOLL_HPP__
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>
using namespace std;
typedef int(*cal_t)(int,int);
class Task{
private:
int x;
int y;
int z;
cal_t handler_task;
public:
Task(int a,int b,cal_t handler_task_)
:x(a),y(b),handler_task(handler_task_)
{
}
void Run()
{
z = handler_task(x,y);
}
void Show()
{
cout<<"thread : "<<pthread_self()<<"Task finish,result is : "<<z<<endl;
}
~Task()
{
}
};
class ThreadPool{
private:
queue<Task> Task_Queue;
bool IsStop;
int ThreadNum;
pthread_mutex_t lock;
pthread_cond_t cond;
private:
static void *route(void *arg)
{
ThreadPool *tp = (ThreadPool*)arg;
pthread_detach(pthread_self());
while(1){
tp->LockQueue();
if(tp->IsEmpty()){
tp->IdleThread();
}
Task t = tp->GetTask();
tp->UnlockQueue();
t.Run();
t.Show();
}
}
void NoticOneThread()
{
pthread_cond_signal(&cond);
}
void NoticAllThread()
{
pthread_cond_broadcast(&cond);
}
public:
ThreadPool(int num):ThreadNum(num),IsStop(false)
{}
void InitThreadPool()
{
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond,NULL);
int i = 0;
for(;i<ThreadNum;i++)
{
pthread_t tid;
pthread_create(&tid,NULL,route,(void *)this);
}
}
~ThreadPool()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
void LockQueue()
{
pthread_mutex_lock(&lock);
}
void UnlockQueue()
{
pthread_mutex_unlock(&lock);
}
bool IsEmpty()
{
return Task_Queue.size() == 0 ? true : false;
}
void AddTask(Task &t)
{
if(IsStop)
{
UnlockQueue();
return ;
}
Task_Queue.push(t);
NoticOneThread();
UnlockQueue();
}
void IdleThread()
{
if(IsStop){
UnlockQueue();
ThreadNum--;
pthread_exit((void *)0);
cout<<"pthread "<<pthread_self() <<"quit"<<endl;
return ;
}
pthread_cond_wait(&cond,&lock);
}
Task GetTask()
{
Task t = Task_Queue.front();
Task_Queue.pop();
return t;
}
void Stop()
{
LockQueue();
IsStop = true;
UnlockQueue();
while(ThreadNum > 0){
NoticAllThread();
}
}
};
#endif
测试文件
我们只设置九个任务。
#include"ThreadPool.hpp"
#define NUM 5
int Add(int x,int y)
{
return x + y;
}
int main()
{
ThreadPool *tp =new ThreadPool(NUM);
tp -> InitThreadPool();
int count =1;
int u = 0;
for(;u<1;u++){
sleep(1);
Task t(count,count-1,Add);
tp->AddTask(t);
count++;
}
return 0;
}
结果
在gdb下观察:
1)首先创建5个线程。
2)5个线程桉序执行任务。
3)没有任务了线程退出。