线程池
- 一.概念
- 二.模拟实现一个线程池
一.概念
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
线程池的应用场景:
- 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。
线程池示例:
- 创建固定数量线程池,循环从任务队列中获取任务对象,
- 获取到任务对象后,执行任务对象中的任务接口
二.模拟实现一个线程池
1.我们要写一个线程池类(ThreadPool),首先要写它的构造和析构,很明显是多个线程,那么需要线程的个数(num),同时保证它们的互斥关系,需要锁(mutex)和条件变量(cond)。
#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>//每个线程的基本信息
struct ThreadInfo
{pthread_t tid;std::string name;
};//。默认线程池里有5个线程
static const int defalutnum = 5;template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
public:ThreadPool(int num=defalutnum): threads_(num){pthread_mutex_init(&mutex,nullptr);//初始化锁pthread_cond_init(&cond,nullptr);//初始化条件变量}~ThreadPool(){pthread_mutex_destroy(&mutex);//销毁锁pthread_cond_destroy(&cond);//销毁条件变量}private:std::vector<ThreadInfo> threads_;///线程池std::queue<T> tasks_;//任务pthread_mutex_t mutex;//锁pthread_cond_t cond;//条件变量
};
2.开始创建线程,给外部留一个Start接口,方便进行使用。
但这里有一个问题:当编译时会发生报错,因为HandlerTask属于ThreadPool的内置函数,所以第一个参数是隐藏的this指针,而线程函数的格式要求参数有且只有一个是void*类型。所以要么将它放在类外,要么就加static。
3.写一个Push函数用来添加任务。同时对上锁,解锁,休眠…进行封装以方便使用。
#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>//每个线程的基本信息
struct ThreadInfo
{pthread_t tid;std::string name;
};//。默认线程池里有5个线程
static const int defalutnum = 5;template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
private:void Lock()//上锁{pthread_mutex_lock(&mutex);}void Unlock()//解锁{pthread_mutex_unlock(&mutex);}void Wakeup()//唤醒{pthread_cond_signal(&cond);}void Threadsleep()//休眠{pthread_cond_wait(&cond,&mutex);}
public:ThreadPool(int num=defalutnum): threads_(num){pthread_mutex_init(&mutex,nullptr);//初始化锁pthread_cond_init(&cond,nullptr);//初始化条件变量}static void* HandlerTask(void* arges){}void Start(){int num=threads_.size();for(int i=0;i<num;i++){threads_[i].name="thread-"+std::to_string(i);//把线程名写入pthread_create(&(threads_[i].tid),nullptr,HandlerTask,nullptr);//创建线程}}void Push(const T&t)//向任务表里添加任务{//保证互斥要上锁Lock();tasks_.push(t);Wakeup();//唤醒一个线程执行任务Unlock();}~ThreadPool(){pthread_mutex_destroy(&mutex);//销毁锁pthread_cond_destroy(&cond);//销毁条件变量}private:std::vector<ThreadInfo> threads_;//线程池std::queue<T> tasks_;//任务pthread_mutex_t mutex;//锁pthread_cond_t cond;//条件变量
};
4.接下来就是多个线程如何执行任务了。
问题:这里编译会直接报错,因为HandlerTask是静态函数,不能直接使用成员变量。解决方法:传this指针。同时前面的方法是私有的,也需要改成公有的。
#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>//每个线程的基本信息
struct ThreadInfo
{pthread_t tid;std::string name;
};//。默认线程池里有5个线程
static const int defalutnum = 5;template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
public:void Lock()//上锁{pthread_mutex_lock(&mutex);}void Unlock()//解锁{pthread_mutex_unlock(&mutex);}void Wakeup()//唤醒{pthread_cond_signal(&cond);}void Threadsleep()//休眠{pthread_cond_wait(&cond,&mutex);}bool Isempty(){return tasks_.empty();}
public:ThreadPool(int num=defalutnum): threads_(num){pthread_mutex_init(&mutex,nullptr);//初始化锁pthread_cond_init(&cond,nullptr);//初始化条件变量}static void* HandlerTask(void* arges){ThreadPool<T>* tp=static_cast<ThreadPool<T>*>args;while(true){//先上锁tp->Lock();while(tp->Isempty())//使用while防止误唤醒{Threadsleep();//如果没有任务先休眠}T t=tp->Pop();//封装一个Pop函数tp->Unlock();//解锁//执行任务//t();//这里我写的样例任务是重载了(),所以直接用括号表示执行任务(大家可以自己任意写任务)}}T Pop(){T t=tasks_.front();tasks_.pop();return t;}void Start(){int num=threads_.size();for(int i=0;i<num;i++){threads_[i].name="thread-"+std::to_string(i);//把线程名写入pthread_create(&(threads_[i].tid),nullptr,HandlerTask,this);//创建线程}}void Push(const T&t)//向任务表里添加任务{//保证互斥要上锁Lock();tasks_.push(t);Wakeup();//唤醒一个线程执行任务Unlock();}~ThreadPool(){pthread_mutex_destroy(&mutex);//销毁锁pthread_cond_destroy(&cond);//销毁条件变量}private:std::vector<ThreadInfo> threads_;//线程池std::queue<T> tasks_;//任务pthread_mutex_t mutex;//锁pthread_cond_t cond;//条件变量
};
整个线程池已经简单的写完了,大家可以自己写一些任务来进行测试啦。