线程池的定义
线程池就是预先把一堆线程封装好,使它变成一个项目的基础组件,当项目需要频繁使用线程来处理各式各样的任务时,可以直接拿这个组件来搭建项目。
使用线程池的原因
线程的创建和销毁相对于进程来说是轻量级的,但是当我们的任务需要进行大量线程的创建和销毁操作时,这些开销合在一起就比较大了。比如,当设计一个压力测试框架时,需要连续产生大量的并发操作。线程池在这种场合是非常使用的。线程池的好处就在于线程复用,某个线程在处理完一个任务后,可以继续处理下一个任务,而不用销毁又创建,这样可以避免无所谓的开销,因此线程池尤其适用于连续产生大量并发任务的场合。
线程池的基本流程
初始线程池里的线程都处于空闲等待状态,当有新的任务需要处理的时候,就从这堆线程中取一个空闲等待的线程来处理任务,当任务处理完毕后,把该线程放回池中(一般就是将线程状态置为空闲),以供后面的任务继续使用。当池子的线程都处于忙碌状态时,可根据需要选择一个新的线程并置于池中,或者通知任务当前线程池里所有线程都在忙,需要等待片刻再尝试。流程图如下:
线程池的设计千变万化,不同的场合有不同的定制化,但是有一些基础的东西是不会变的,任何复杂的线程池,都是从基本的功能开始扩展。
基于POSIX实现线程池
thread_pool.h
#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_
#include <vector>
#include <string>
#include <pthread.h>
using namespace std;
/*
使用策略者设计模式:
稳定点:线程池、线程池中的线程执行任务
变化点:具体的任务
总结:使用两个类,一个是任务的抽象,一个封装线程池。在线程池中使用组合的方法,联系任务类,
具体任务可以使用继承的方法。
*/
/*执行任务的类:设置任务数据并执行*/
class CTask {
protected:
string m_strTaskName; //任务的名称
void* m_ptrData; //要执行的任务的具体数据
public:
CTask() = default;
CTask(string &taskName)
: m_strTaskName(taskName)
, m_ptrData(NULL) {}
virtual int Run() = 0;
void setData (void* data); //设置任务数据
virtual ~CTask() {}
};
/*线程池管理类*/
class CThreadPool {
private:
static vector<CTask*> m_vecTaskList; //任务列表(组合)
static bool shutdown; //线程退出标志
int m_iThreadNum; //线程池中启动的线程数
pthread_t* pthread_id;
static pthread_mutex_t m_pthreadMutex; //线程同步锁
static pthread_cond_t m_pthreadCond; //线程同步条件变量
protected:
static void* ThreadFunc(void* threadData); //新线程的线程回调函数
static int MoveToIdle(pthread_t tid); //线程执行结束后,把自己放入空闲线程中
static int MoveToBusy(pthread_t tid); //移入到忙碌线程中去
int Create(); //创建线程池中的线程
public:
CThreadPool(int threadNum);
int AddTask(CTask* task); //把任务添加到任务队列中
int StopAll(); //使线程池中的所有线程退出
int getTaskSize(); //获取当前任务队列中的任务
};
#endif
thread_pool.cpp
#include "lgh_threadPool.h"
#include <cstdio>
void CTask::setData(void* data){
m_ptrData = data;
}
//静态成员初始化
vector<CTask*> CThreadPool::m_vecTaskList;
bool CThreadPool::shutdown = false;
pthread_mutex_t CThreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t CThreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER;
//线程管理类构造函数
CThreadPool::CThreadPool(int threadNum){
this->m_iThreadNum = threadNum;
printf("I will create %d threads \n", threadNum);
Create();
}
//线程回调函数
void* CThreadPool::ThreadFunc(void* threadData){
pthread_t tid = pthread_self();
while(1){
pthread_mutex_lock(&m_pthreadMutex);
//如果队列为空,等待新的任务进入任务队列
while(m_vecTaskList.size() == 0 && !shutdown)
pthread_cond_wait(&m_pthreadCond, &m_pthreadMutex);
//关闭线程
if(shutdown){
pthread_mutex_unlock(&m_pthreadMutex);
printf("[tid: %lu]\texit\n",pthread_self());
pthread_exit(NULL);
}
printf("[tid: %lu]\trun: ",tid);
vector<CTask*>::iterator iter = m_vecTaskList.begin();
//从队列首部取一个任务处理
CTask* task = *iter;
if(iter != m_vecTaskList.end()){
task = *iter;
m_vecTaskList.erase(iter);
}
pthread_mutex_unlock(&m_pthreadMutex);
task->Run();
printf("[tid: %lu]\tidle\n", tid);
}
return (void*)0;
}
//往任务队列里添加任务并发出线程同步信号
int CThreadPool::AddTask(CTask* task){
pthread_mutex_lock(&m_pthreadMutex);
m_vecTaskList.push_back(task);
pthread_mutex_unlock(&m_pthreadMutex);
pthread_cond_signal(&m_pthreadCond);
return 0;
}
//创建线程
int CThreadPool::Create(){
pthread_id = new pthread_t[m_iThreadNum];
for(int i = 0; i<m_iThreadNum; i++){
pthread_create(&pthread_id[i], NULL, ThreadFunc, NULL);
}
return 0;
}
//停止所有线程
int CThreadPool::StopAll(){
//避免重复调用
if(shutdown)
return -1;
printf("Now I will end all threads!\n\n");
//唤醒所有等待的线程,线程池摧毁
shutdown = true;
pthread_cond_broadcast(&m_pthreadCond);
//清楚僵尸线程
for(int i = 0; i<m_iThreadNum; i++){
pthread_join(pthread_id[i], NULL);
}
delete[] pthread_id;
pthread_id = NULL;
//销毁锁
pthread_mutex_destroy(&m_pthreadMutex);
pthread_cond_destroy(&m_pthreadCond);
return 0;
}
//获取当前队列中的任务
int CThreadPool::getTaskSize(){
return m_vecTaskList.size();
}
main.cpp
#include "lgh_threadPool.h"
#include <cstdio>
#include <stdlib.h>
#include <unistd.h>
class CMyTask : public CTask{
public:
CMyTask()= default;
int Run(){
printf("%s\n", (char*)m_ptrData);
int x = rand() % 4+1;
sleep(x);
return 0;
}
~CMyTask(){}
};
int main(){
CMyTask taskObj;
char szTmp[] = "hello!";
taskObj.setData((void*)szTmp);
CThreadPool threadpool(5);
for(int i = 0; i < 10; i++){
threadpool.AddTask(&taskObj);
}
while(1){
printf("There are still %d tasks need to handle\n", threadpool.getTaskSize());
// 若任务队列已经没有任务了
if(threadpool.getTaskSize() == 0){
//清楚线程池
if(threadpool.StopAll() == -1){
printf("Thread pool clear, exit. \n");
exit(0);
}
}
sleep(2);
printf("2 seconds later... \n");
}
return 0;
}
运行结果:
I will create 5 threads
There are still 10 tasks need to handle
[tid: 139622839002880] run: hello!
[tid: 139622830610176] run: hello!
[tid: 139622822217472] run: hello!
[tid: 139622855788288] run: hello!
[tid: 139622847395584] run: hello!
2 seconds later...
There are still 5 tasks need to handle
[tid: 139622847395584] idle
[tid: 139622847395584] run: hello!
[tid: 139622822217472] idle
[tid: 139622822217472] run: hello!
[tid: 139622830610176] idle
[tid: 139622830610176] run: hello!
[tid: 139622855788288] idle
[tid: 139622855788288] run: hello!
[tid: 139622839002880] idle
[tid: 139622839002880] run: hello!
2 seconds later...
There are still 0 tasks need to handle
Now I will end all threads!
[tid: 139622830610176] idle
[tid: 139622830610176] exit
[tid: 139622822217472] idle
[tid: 139622822217472] exit
[tid: 139622847395584] idle
[tid: 139622855788288] idle
[tid: 139622839002880] idle
[tid: 139622839002880] exit
[tid: 139622855788288] exit
[tid: 139622847395584] exit
2 seconds later...
There are still 0 tasks need to handle
Thread pool clear, exit.
注意:每次运行的结果都会不一样。
详细可参考:《Linux C/C++ 服务器开发实践》——朱文伟 李建英