线程池总体流程
线程池:一次性创建多个子线程,主线程(生产者)负责接收客户端的链接并创建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);
}
}