c++实现一个高并发服务器(二)线程池

线程池的实现

在上次我实现了线程池的一个组件:任务队列。这次来实现线程池。

线程池的概念

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。

为什么要用线程池

直接创建销毁线程的缺点
  • 我们使用线程的目的本是出于效率考虑,可以为了创建这些线程却消耗了额外的时间,资源,对于线程的销毁同样需要系统资源。

  • cpu资源有限,上述代码创建线程过多,造成有的任务不能即时完成,响应时间过长。

  • 线程无法管理,无节制地创建线程对于有限的资源来说似乎成了“得不偿失”的一种作用。

线程池的好处
  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

  • 提高线程的可管理性。

书接上回
c++实现一个高并发服务器(一)

线程池的组件

成员作用
ThreadPool(int minNum,int maxNum)输入最小和最大线程数来创建一个线程池
~ThreadPool()析构函数。回收管理员线程和所有存活的工作线程,销毁互斥锁和条件变量。
void addTask()添加任务的接口。
int getBusyNum()获得工作中的线程的数量。
int getAliveNum();获得存活的线程的数量。
static void* worker(void* arg)工作线程的执行函数。
static void* manager(void* arg)管理员线程的执行函数。负责动态的添加或删除工作线程。
void ThreadExit()线程退出函数。
TaskQueue* m_tq任务队列,存放待执行任务
pthread_mutex_t m_mutex互斥锁,保证线程同步
pthread_cond_t m_not_empty条件变量,任务队列为空时工作线程阻塞并等待此条件变量,用于在要回收工作线程和有添加新任务时发出信号唤醒线程。
pthread_t* m_pworkerThreadId工作线程的指针,用于申请动态内存存放一组工作线程。
pthread_t m_managerThreadId管理员线程
int m_minNum线程池最小线程数量
int m_maxNum线程池最大线程数量
int m_busyNum线程池中执行任务的工作线程的数量
int m_aliveNum线程池中存活的工作线程的数量
int m_exitNum线程池中等待退出的工作线程的数量。
bool m_shutdown=false线程池关闭标志

关键部分讲解

  • 管理员线程:管理员线程在线程池存在时就一直检测线程池(每秒检测一次),在检测期间做如下任务:

    • 加锁获取线程池的执行任务线程数量和任务数
    • 添加线程策略/算法(当前任务个数>存活的线程数 && 存活的线程数<最大线程个数),线程池加锁,然后向线程池中加工作线程
    • 销毁多余的线程(忙线程*2 < 存活的线程数目 && 存活的线程数 > 最小线程数量)
  • 工作线程:在线程池创建时被创建,用一个线程数组保存,没有任务时阻塞,有任务时取出任务并执行。

    • 访问任务队列(共享资源)时加锁
    • 判断任务队列是否为空,如果为空则工作线程阻塞(等待条件变量),解除阻塞(收到条件变量,但有可能是要求销毁线程)先判断是否要销毁线程(如果待退出的线程数大于0则需要退出线程,另外如果线程池关闭标志为真也需要退出),然后从任务队列中取出一个任务并执行。循环此过程。
  • 线程回收

    • 在线程池关闭或管理员线程销毁多于线程时执行
    • 调用threadExit(pthread_self())
  • 线程池创建

    • 实例化任务队列,初始化各成员变量
    • 根据线程池最大数量给线程数组分配内存
    • 初始化条件变量和互斥锁
    • 创建工作线程和管理员线程
  • 线程池析构

    • 将线程池关闭标志置为真,先销毁管理员线程
    • 再唤醒所有工作线程让他们自己结束。
    • 销毁条件变量、互斥锁

代码实现

ThreadPool.h

#pragma once

#include"TaskQueue.h"
class ThreadPool{
public:
	ThreadPool(int minNum,int maxNum);
	~ThreadPool();
	void addTask(Task &task);
	int getBusyNum();
	int getAliveNum();
private:
	static void* worker(void* arg);
	static void* manager(void* arg);
	void ThreadExit();
	TaskQueue* m_tq;
	pthread_mutex_t m_mutex;
	pthread_cond_t m_not_empty;
	pthread_t* m_pworkerThreadId;
	pthread_t m_managerThreadId;
	int m_minNum;
	int m_maxNum;
	int m_busyNum;
	int m_aliveNum;
	int m_exitNum;
	bool m_shutdown=false;
};

ThreadPool.cpp

#include"ThreadPool.h"

#include<iostream>
#include<string.h>
#include<unistd.h>
#include<string>

using namespace std;

ThreadPool::ThreadPool(int minNum,int maxNum) {
	m_tq=new TaskQueue;
	do{
		m_minNum = minNum;
		m_maxNum = maxNum;
		m_busyNum=0;
		m_aliveNum=minNum;
		m_exitNum=0;

		m_pworkerThreadId=new pthread_t[maxNum];
		if(m_pworkerThreadId==nullptr){
			cout<<"malloc thread_t[] failed..."<<endl;
			break;
		}
		memset(m_pworkerThreadId,0,sizeof(pthread_t)*maxNum);
		if(pthread_mutex_init(&m_mutex,NULL)!=0||pthread_cond_init(&m_not_empty,NULL)!=0){
			cout<<"init mutex or condition failed"<<endl;
			break;

		}

		//create threads
		for(int i=0;i<minNum;i++){
			cout<<"create thread..."<<endl;
			pthread_create(&m_pworkerThreadId[i],NULL,worker,this);
		}
		pthread_create(&m_managerThreadId,NULL,manager,this);

	}while(0);

}

ThreadPool::~ThreadPool(){
	m_shutdown=1;

	pthread_join(m_managerThreadId,NULL);
	cout<<"manager thread is destroyed"<<endl;

	for(int i=0;i<m_aliveNum;i++){
		pthread_cond_signal(&m_not_empty);
	}
	cout<<"worker thread is destroyed"<<endl;

	if(m_tq)delete m_tq;
	if(m_pworkerThreadId)delete m_pworkerThreadId;;
	pthread_mutex_destroy(&m_mutex);
	pthread_cond_destroy(&m_not_empty);
	cout<<"thread pool destroyed"<<endl;
}

void ThreadPool::addTask(Task &task){
	if(m_shutdown){
		return;
	}
	cout<<"Add task to pool"<<endl;
	m_tq->addTask(task);
	pthread_cond_signal(&m_not_empty);
}
int ThreadPool::getAliveNum(){
	int threadNum = 0;
	pthread_mutex_lock(&m_mutex);
	threadNum=m_aliveNum;
	pthread_mutex_unlock(&m_mutex);
	return threadNum;
}

int ThreadPool::getBusyNum(){
	int busyNum =0;
	pthread_mutex_lock(&m_mutex);
	busyNum=m_busyNum;
	pthread_mutex_unlock(&m_mutex);
	return busyNum;
}
	
void* ThreadPool::worker(void* arg){
	ThreadPool* pool=static_cast<ThreadPool*>(arg);
	while(true){
		pthread_mutex_lock(&pool->m_mutex);

		while(pool->m_tq->size()==0&&!pool->m_shutdown){
			cout<<"worker thread"<<to_string(pthread_self())<<"waiting..."<<endl;
			pthread_cond_wait(&pool->m_not_empty,&pool->m_mutex);
			if(pool->m_exitNum>0){
				//there are some thread need to be destroied
				pool->m_aliveNum--;
				pthread_mutex_unlock(&pool->m_mutex);
				pool->ThreadExit();
			}
		}
		if(pool->m_shutdown){
			pthread_mutex_unlock(&pool->m_mutex);
			pool->ThreadExit();
		}
		Task task=pool->m_tq->takeTask();
		pool->m_busyNum++;
		pthread_mutex_unlock(&pool->m_mutex);
		cout<<"worker thread "<<to_string(pthread_self())<<"start working..."<<endl;
		task.function(task.arg);
		cout<<"worker thread"<<to_string(pthread_self())<<"end working..."<<endl;
		pthread_mutex_lock(&pool->m_mutex);
		pool->m_busyNum--;
		pthread_mutex_unlock(&pool->m_mutex);		
	}
	return nullptr;
}

void* ThreadPool::manager(void* arg){
	ThreadPool *pool=static_cast<ThreadPool*>(arg);
	while(!pool->m_shutdown){
		sleep(1);
		pthread_mutex_lock(&pool->m_mutex);
		int queueSize=pool->m_tq->size();
		int liveNum=pool->m_aliveNum;
		int busyNum=pool->m_busyNum;
		pthread_mutex_unlock(&pool->m_mutex);

		const int NUMBER=2;//add 2 worker thread at once
		if(queueSize>liveNum&&liveNum<pool->m_maxNum){
			pthread_mutex_lock(&pool->m_mutex);
			int num=0;
			for(int i=0;i<pool->m_maxNum&&num<NUMBER;++i){
				if(pool->m_pworkerThreadId[i]==0){
					pthread_create(&pool->m_pworkerThreadId[i],NULL,worker,pool);
					num++;
					pool->m_aliveNum++;

				}
			}
			pthread_mutex_unlock(&pool->m_mutex);

		}
		//destroy rest of threads
		if(busyNum*2<liveNum&&liveNum>pool->m_minNum){
			pthread_mutex_lock(&pool->m_mutex);
			pool->m_exitNum=NUMBER;
			pthread_mutex_unlock(&pool->m_mutex);
			for(int i=0;i<NUMBER;i++){
				pthread_cond_signal(&pool->m_not_empty);
			}
		}

	}
	return nullptr;
}

void ThreadPool::ThreadExit(){
	pthread_t tid=pthread_self();
	for(int i=0;i<m_maxNum;i++){
		if(m_pworkerThreadId[i]==tid){
			cout<<"ThreadExit() :thread "<<to_string(pthread_self())<<"exiting..."<<endl;
			m_pworkerThreadId[i]=0;
			break;
		}
	}
	pthread_exit(NULL);
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、本课程是一个干货课程,主要讲解如何封装服务器底层,使用Tcp/ip长连接,IDE使用vs2019 c++开发以及使用c++11的一些标准,跨平台windows和linux,服务器性能高效,单服务器压力测试上万无压力,服务器框架是经历过上线产品的验证,框架简单明了,不熟悉底层封装的人,半个小时就能完全掌握服务器框架上手写业务逻辑。2、本课程是一个底层服务器框架教程,主要是教会学员在windows或linux下如何封装一个高效的,避免踩坑的商业级框架,服务器底层使用初始化即开辟内存的技术,使用内存池,服务器运行期间内存不会溢出,非常稳定,同时服务器使用自定义哈希hashContainer,在处理新的连接,新的数据,新的封包,以及解包,发包,粘包的过程,哈希容器性能非常高效,增、删、查、改永远不会随着连接人数的上升而降低性能,增、删、查、改的复杂度永远都是恒定的O(1)。3、服务器底层封装没有使用任何第三方网络库以及任何第三方插件,自由度非常的高,出了任何BUG,你都有办法去修改,查找问题也非常方便,在windows下使用iocp,linux下使用epoll.4、讲解c++纯客户端,主要用于服务器之间通信,也就是说你想搭建多层结构的服务器服务器服务器之间使用socket通信。还可以使用c++客户端做压力测试,开辟多线程连接服务器,教程提供了压力测试,学员可以自己做压力测试服务器性能。5、赠送ue4和unity3d通信底层框架以及多人交互demo,登录,注册,玩家离开,同步主要是教会学员服务器与客户端如何交互。6、赠送c++连接mysql数据库框架demo,登录,注册,玩家离开数据持久化.7、服务器教程使用自定义通信协议,同时也支持protobuf,选择权在开发者自己手里,想用什么协议都可以,自由度高。8、服务器教程使用手动敲代码逐句讲解的方式开展教学课程。非喜勿喷,谢谢大家。9、服务器教程提供源码,大家可以在平台提供的地址下载或者联系我,服务器使用c++11部分标准,std::thread,条件变量,线程锁,智能指针等,需要学员具备一定c++知识,购买前请慎重考虑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值