一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
线程池的应用场景:
线程池示例:
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;//条件变量
};
整个线程池已经简单的写完了,大家可以自己写一些任务来进行测试啦。