1. 什么是线程池
池是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配。当服务器进入正式运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配。很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的。当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用来释放资源。
2. 优点
1. 线程池避免了在处理短时间任务时创建与销毁线程的代价。
2. 线程池不仅能够保证内核充分利用,还能防止过分调度。
注意: 线程池中可用线程的数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
1)需要大量的线程来完成任务,且完成任务的时间比较短。线程池的好处是避免了繁琐的创建和结束线程的时间,有效的利用了CPU资源。 比如WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大>但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2)对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3)接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
3. 应用场景
线程池常见的应用场景如下:
- 需要大量的线程来完成任务,且完成任务的时间比较短。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
4. 线程池的实践
三要素:
1. 线程队列
存放被创建的线程, 这些线程被分为两种状态这两种状态的线程使用两条链表存储
1. 正在执行任务的线程 (忙碌)
2. 等待分配任务的线程 (空闲)
2. 任务队列
使用队列的方式存储添加进来的线程, 添加时会通知空闲队列取任务执行。
3. 控制器
一个线程互斥量以及一个线程条件变量(使用两个控制器达到线程进行控制 进行阻塞或则唤醒)
当任务队列中有一个任务时,在线程队列中还有空闲的线程则会唤醒一个线程去完成任务
5 . 线程池的流程
6. 实现代码
第一创建一个线程要执行的任务基类, 不同任务创建派生类并继承该基类就可实现不同任务。
#pragma once
/********************************************
文件: CBasetast.h
作者: yida_li
日期: 2022/07/23 11 : 15
功能: 事件基类
*********************************************/
#include <string.h>
class CBasetast
{
public:
CBasetast(char * tast);
virtual ~CBasetast();
virtual void workTast() = 0;
private:
char buf[1024];
};
#include "CBasetast.h"
CBasetast::CBasetast(char* tast)
{
memcpy(this->buf, tast, sizeof(this->buf));
}
CBasetast::~CBasetast()
{
delete this->buf;
}
其次创建一个任务基类的派生类
#pragma once
/********************************************
文件: WorkTast1.h
作者: yida_li
日期: 2022/07/23 11 : 32
功能: 工作类 1
*********************************************/
#include <iostream>
#include <string.h>
#include <unistd.h>
#include "CBasetast.h"
using namespace std;
/********************************************
文件: WorkTast1.h
作者: yida_li
日期: 2022/07/26 1 : 41
功能: 任务派生类
*********************************************/
class WorkTast1 :
public CBasetast
{
public:
WorkTast1(char* tast);
~WorkTast1();
void workTast(); //工作函数
private:
char buf[1024];
};
#include "WorkTast1.h"
WorkTast1::WorkTast1(char* tast):CBasetast(tast)
{
memcpy(this->buf, tast, sizeof(this->buf));
}
WorkTast1::~WorkTast1()
{
}
void WorkTast1::workTast()
{
cout << " 正在执行任务中 。。。 " << this->buf << endl;
}
最后创建最重要的 线程的实现类
#pragma once
#include <iostream>
#include <queue>
#include <list>
#include <algorithm>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "CBasetast.h"
using namespace std;
#define CREAT_MIN_NUM 10
#define CREAT_MAX_NUM 30
class CThreadPool
{
public:
CThreadPool( const int creat_num = CREAT_MIN_NUM);
~CThreadPool();
//判断任务队列是否为空
bool QueueIsEmpty();
//互斥量操作
void Lock();
void unLock();
//条件变量操作
void wait();
void wakeUp();
static void* RunTime(void* pv);//线程执行函数
void pushTask(CBasetast* task);//添加任务到任务队列
CBasetast* popTask();//从任务队列移除任务
void MoveToIdle(pthread_t id);//移动到空闲链表
void MoveToBusy(pthread_t id);//移动到忙碌链表
private:
int threadMinNum;//最小线程数量
int threadMaxNum;//最大线程数量
queue<CBasetast*>taskQueue;//任务队列
list<pthread_t>busyList;//忙碌链表
list<pthread_t>idleList;//空闲链表
pthread_mutex_t mutex;//线程互斥量作用:做锁
pthread_cond_t cond;//线程条件变量作用:让线程出现阻塞或唤醒
};
#include "CThreadPool.h"
//构造函数
CThreadPool::CThreadPool(const int creat_num)
{
this->threadMinNum = creat_num;
//互斥量初始化
pthread_mutex_init(&this->mutex , NULL);
//条件变量初始化
pthread_cond_init(&this->cond, NULL);
for (int i = 0; i < this->threadMinNum ; i++)
{
pthread_t id;
pthread_create(&id, NULL, RunTime, this);
this->idleList.push_back(id);
}
}
CThreadPool::~CThreadPool()
{
}
bool CThreadPool::QueueIsEmpty()
{
return this->taskQueue.empty();
}
//互斥量锁
void CThreadPool::Lock()
{
pthread_mutex_lock(&this->mutex);
}
//互斥量解锁
void CThreadPool::unLock()
{
pthread_mutex_unlock(&this->mutex);
}
//条件变量等待(阻塞)
void CThreadPool::wait()
{
pthread_cond_wait(&this->cond, &this->mutex);
}
//条件变量唤醒, 解除掉线程的阻塞状态,使线程开始执行任务
void CThreadPool::wakeUp()
{
pthread_cond_signal(&this->cond);
}
//运行任务
void* CThreadPool::RunTime(void* pv)
{
//某线程在执行过程中先获取到自己的id,以便之后操作移动到空闲移动到忙碌的动作
pthread_t id = pthread_self();
//确保主线程与当前执行线程逻辑完全分离,当前执行线程执行结束后,id会自动释放
//分离目的:为了声明这个线程不会阻塞主线程的逻辑,pthread_detach不会终止当前执行线程pthread_detach(id);
CThreadPool* argThis = (CThreadPool*)pv;
while (true)
{
argThis->Lock();
while (argThis->QueueIsEmpty()) {
argThis->wait();
}
cout << "工作执行前任务数:" << argThis->taskQueue.size() << endl;
cout << "工作执行前busy: " << argThis->busyList.size() << endl;
cout << "工作执行前idle: " << argThis->idleList.size() << endl;
cout << "------------------这是执行任务前的---------------------------------------------------- - " << endl;
argThis->MoveToBusy(id);
//取任务
CBasetast* task = argThis->popTask();
argThis->unLock();
//任务工作
task->workTast();
argThis->Lock();
argThis->MoveToIdle(id);
cout << endl;
cout << "工作执行后任务数:" << argThis->taskQueue.size() << endl;
cout << "工作执行后busy: " << argThis->busyList.size() << endl;
cout << "工作执行后idle: " << argThis->idleList.size() << endl;
cout << " ----------------------------以上是任务执行后的----------------------------------------- " << endl;
argThis->unLock();
}
CThreadPool* pp = (CThreadPool*)pv;
return pp;
//return nullptr;
}
//添加任务进任务队列
void CThreadPool::pushTask(CBasetast* task)
{
Lock(); //锁住
this->taskQueue.push(task);
unLock(); //解锁
wakeUp(); // 任务成功添加进任务队列中,之后开始唤醒线程开始工作执行任务
}
//从任务列表中取出任务,并释放掉队头
CBasetast* CThreadPool::popTask()
{
CBasetast* pp = this->taskQueue.front(); //取出任务队列头赋值给一个临时的任务类对象
this->taskQueue.pop(); //取出任务后手动释放掉
return pp;
}
//从忙碌链表到空闲空闲链表
void CThreadPool::MoveToIdle(pthread_t id)
{
list<pthread_t>::iterator iterator1;
iterator1 = find(this->busyList.begin(), this->busyList.end(), id);
if (iterator1 != this->busyList.end())
{
this->busyList.erase(iterator1); //取出空闲链表
this->idleList.push_back(*iterator1); //插入 iterator1 到忙碌链表
}
}
//从空闲链表到忙碌链表
void CThreadPool::MoveToBusy(pthread_t id)
{
list<pthread_t>::iterator iterator1;
iterator1 = find(this->idleList.begin(), this->idleList.end(), id);
if (iterator1 != this->idleList.end())
{
this->idleList.erase(iterator1); //从忙碌队列中删除掉 iterator1
this->busyList.push_back(*iterator1); // 插入iterator1 到空闲链表中
}
}