C++手撕一个线程池

线程池总体流程

线程池:一次性创建多个子线程,主线程(生产者)负责接收客户端的链接并创建socket对象然后加入到仓库中,然后子线程(消费者)从仓库中获取socket对象进行服务,当客户端退出时,子线程不销毁而是继续从仓库中获取下一个socket对象进行服务。这种网络编程的优点是限制客户端的链接数量,不需要创建销毁线程,节约了线程销毁和创建的时间,同时空闲的线程会进入休眠,不会与工作线程竞争。所以它的应用范围更广,编程难度更高。

在这里插入图片描述
线程池中的主线程抓取任务到仓库和子线程消费任务的模块,与消费者与生产者模型非常相似。
看懂了消费者与生产者模型也可以助你更加轻松理解线程池。
消费者和生产者模型详解

线程池代码

#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <iostream>
#include <pthread.h>
#include <queue>
#include <stdio.h>

using namespace std;

//任务入口函数
typedef void (*EnterFP)(void* arg);

template <typename T>
class ThreadPool
{
	int thread_cnt;				//子线程数量
	int store_cal;				//仓库容量
	pthread_t* tids;			//子线程线程号
	queue<T> store;				//仓库任务队列
	EnterFP enter;				//任务入口函数
	pthread_mutex_t* lock;		//线程锁
	pthread_cond_t* empty;		//仓库为空的条件变量
	pthread_cond_t* full;		//仓库为满的条件变量
public:
    //线程池初始化
	ThreadPool(EnterFP enter,int thread_cnt = 3,int store_cal =5)
	{
		this->enter = enter;
		this->thread_cnt = thread_cnt;
		this->store_cal = store_cal;
		tids = new pthread_t[thread_cnt];
		
		lock = new pthread_mutex_t;
		pthread_mutex_init(lock,NULL);
		empty = new pthread_cond_t;
		pthread_cond_init(empty,NULL);
		full = new pthread_cond_t;
		pthread_cond_init(full,NULL);
	}
	
    //线程池退出
	~ThreadPool()
	{
		for(int i=0; i<thread_cnt; i++)
		{
			pthread_cancel(tids[i]);
			cout << "线程:" << tids[i] << "已经取消!" << endl; 
		}
		
		delete[] tids;
		
		pthread_mutex_destroy(lock);
		delete lock;
		
		pthread_cond_destroy(empty);
		delete empty;
		
		pthread_cond_destroy(full);
		delete full;
	}
	
    //线程池启动函数
	void start(void)
	{
		for(int i=0; i<thread_cnt; i++)
		{
			pthread_create(&tids[i],NULL,run,this);
			cout << "线程:" << tids[i] << "创建成功" << endl;
		}
	}
	
    //子线程线程从仓库抓取任务
	static void* run(void* arg)
	{
		ThreadPool* threadPool = static_cast<ThreadPool*>(arg);
		while(true)
		{
			T task = threadPool->pop();
			threadPool->enter(task);
		}
	}
	
    //主线程添加任务到仓库中
	void push(T task)
	{
		pthread_mutex_lock(lock);
		while(store_cal <= store.size())
		{
			pthread_cond_wait(full,lock);
		}
		
		store.push(task);
		pthread_mutex_unlock(lock);
		pthread_cond_signal(empty);
	}
	
    //仓库弹出任务并分配给子线程处理
	T pop(void)
	{
		pthread_mutex_lock(lock);
		while(0 == store.size())
		{
			pthread_cond_wait(empty,lock);
		}
		T task = store.front();
		store.pop();
		pthread_mutex_unlock(lock);
		pthread_cond_signal(empty);
		return task;
	}
};

#endif//THREADPOOL_H

测试代码

TCP服务端测试代码

#include "threadpool.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

ThreadPool<int*>* threadPool;

void sigint(int signum)
{
	printf("服务器退出!\n");
	delete threadPool;
	exit(0);
}

void enter(void* arg)
{
	int fd = *(int*)arg;
	printf("enter!\n");
	char buf[4096] = {};
	size_t size = sizeof(buf);

	while(true)
	{
		int ret = recv(fd,buf,size,0);
		if(0 >= ret || 0 == strcmp("quit",buf))
		{
			break;
		}

		strcat(buf,":return");
		ret = send(fd,buf,strlen(buf)+1,0);
		if(0 >= ret)
		{
			break;
		}
	}

	close(fd);
	printf("客户端%d退出!\n",fd);
	return;
}

int main(int argc,const char* argv[])
{
	signal(SIGINT,sigint);
	int svr_fd = socket(AF_INET,SOCK_STREAM,0);
	if(0 > svr_fd)
	{
		perror("socket");
		return -1;
	}

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6789);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	socklen_t addrlen = sizeof(addr);

	if(bind(svr_fd,(sockaddr*)&addr,addrlen))
	{
		perror("bind");
		return -1;
	}

	if(listen(svr_fd,5))
	{
		perror("listen");
		return -1;
	}

	threadPool = new ThreadPool<int*>(enter);
	threadPool->start();

	while(true)
	{
		int cli_fd = accept(svr_fd,(sockaddr*)&addr,&addrlen);
		if(0 > cli_fd)
		{
			perror("accept");
			continue;
		}
		threadPool->push(&cli_fd);
	}
}

TCP客户端测试代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUF_SIZE (4096)

int main()
{
	// 创建socket对象
	int cli_fd = socket(AF_INET,SOCK_STREAM,0);
	if(0 > cli_fd)
	{
		perror("socket");
		return -1;
	}

	// 准备通信地址(服务端)
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6789);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	// 连接服务端
	if(connect(cli_fd,(struct sockaddr*)&addr,sizeof(addr)))
	{
		perror("connect");
		return -1;
	}

	char buf[4096];
	for(;;)
	{
		printf(">>");
		gets(buf);
		int send_size = write(cli_fd,buf,strlen(buf)+1);
		if(0 >= send_size || 0 == strcmp(buf,"quit"))
		{
			printf("结束通信!\n");
			close(cli_fd);
			return 0;
		}

		int recv_size = read(cli_fd,buf,BUF_SIZE);
		if(0 >= recv_size)
		{
			printf("结束通信!\n");
			close(cli_fd);
			return 0;
		}
		printf("read:[%s],size:%d\n",buf,recv_size);
	}
}

cmp(buf,“quit”))
{
printf(“结束通信!\n”);
close(cli_fd);
return 0;
}

	int recv_size = read(cli_fd,buf,BUF_SIZE);
	if(0 >= recv_size)
	{
		printf("结束通信!\n");
		close(cli_fd);
		return 0;
	}
	printf("read:[%s],size:%d\n",buf,recv_size);
}

}


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值