线程池

今天我们来看一下线程池的概念。首先什么是池呢?作用是什么?

池:初始时,申请比刚开始要使用的资源大的多的资源空间。接下来使用时,直接从池中获取资源。

线程池:多线程存在如果客户端链接,创建一个新的线程,客户端关闭,释放线程。服务器更多时间消耗在创建线程、释放线程。对于业务逻辑的处理,就会较少。所以,我们可以用线程池来改善问题。

总的来说线程池就是在服务器运行初始时,创建n 个线程,将这个n 个线程用池管理起来,主线程负责监听套接字、接受客户连接。工作线程负责和客户端具体通讯。当有用户连接时,从线程池中选取一个线程为其服务,客户端关闭以后,服务器就将线程又放回到池中。

线程池的实现:(这里以三条为例)

1、主线程执行先创建3 条线程;

2、主线程等待客户连接,3 条函数线程因为信号量的P 操作阻塞运行;

3、主线程接受到客户连接后,通过信号量的V 操作通知一个函数线程和客户端通讯。


现在就有一个问题:主线程怎样将连接的文件描述符传递给函数线程??

答:全局数组作为等待函数线程处理的文件描述符的等待队列。

下面我们来看看代码实现:

ser.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>

#define  MAX  10
int fds[MAX];

sem_t sem;


void Init_fds()
{
	int i = 0;
	for(; i < MAX; ++i)
	{
		fds[i] = -1;
	}
}

int Add_fds(int c)
{
	int i = 0;
	for(; i < MAX; ++i)
	{
		if(fds[i] == -1)
		{
			fds[i] = c;
			return 1;
		}
	}

	if(i == MAX)
	{
		return 0;
	}
}

void Del_fds(int i)
{
	for(; i < MAX - 1; ++i)
	{
		fds[i] = fds[i + 1];

		if(fds[i+1] == -1)
		{
			break;
		}
	}
	fds[MAX - 1] = -1;
}

int Get_fds()
{
	pthread_mutex_lock(&mutex);

	int i = 0;
	for(; i < MAX; ++i)
	{
		if(fds[i] != -1)
		{
			int c = fds[i];
			Del_fds(i);
			return c;
		}
	}
}

void *pthread_fun(void *arg)
{
	while(1)  
	{
		sem_wait(&sem);
		int c = Get_fds();
		if(c == -1)
		{
			continue;
		}
		while(1)
		{
			char buff[128] = {0};
			int n = recv(c, buff, 127, 0);
			if(n <= 0)
			{
				close(c);
				break;
			}

			printf("cli: %d ; buff: %s\n", c, buff);
			send(c, "OK", 2, 0);
		}
	}
}

int main(int argc, char *argv[])
{
	Init_fds();
	sem_init(&sem, 0, 0);
	int i = 0;
	for(; i < 3; ++i)
	{
		pthread_t id;
		int res = pthread_create(&id, NULL, pthread_fun, NULL);
		assert(res == 0);
	}

	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(sockfd != -1);

	struct sockaddr_in ser, cli;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("192.168.1.120");

	int res = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
	assert(res != -1);

	listen(sockfd, 5);

	while(1)
	{
		socklen_t len = sizeof(cli);
		int c = accept(sockfd, (struct sockaddr*)&cli, &len);
		if(c < 0)
		{
			printf("error\n");
			continue;
		}

		if(!Add_fds(c))
		{
			send(c, "please wait...", strlen("please wait..."), 0);
			close(c);
			continue;
		}
		sem_post(&sem);
	}

	return 0;
}
cli.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>

void main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(sockfd != -1);

	struct sockaddr_in ser, cli;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6500);
	ser.sin_addr.s_addr = inet_addr("192.168.1.120");

	int res = connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));
	assert(res != -1);

	while(1)
	{
		printf("please input: ");
		fflush(stdout);
		char buff[128] = {0};
		fgets(buff, 128, stdin);
		if(strncmp(buff, "end", 3) == 0)
		{
			close(sockfd);
			break;
		}

		send(sockfd, buff, strlen(buff) - 1, 0);
		memset(buff, 0, 128);
		recv(sockfd, buff, 127, 0);
		printf("%s\n", buff);
	}
}
执行结果:


可以看到我们可以开启多个客户端,并且服务器可以连接并且进行数据接受发送,不过当开启第四个客户端时,服务器端并没有显示发送的数据,因为我们这里三个函数线程全部被占用,没有多余的来给第四个客户端服务,所以它就一直阻塞在recv,不过只要结束一个客户端,第四个客户端发送的数据便可以显示。

当我输入“end”结束掉第一个客户端,可以看到第四个客户端的数据便显示出来了。


不过我们这个程序还是不安全的,万一当你在get的时候进行了add,这个时候有可能会造成错误。所以我们做出如下更改:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>

#define MAX 10
int fds[MAX];

sem_t sem;
pthread_mutex_t mutex;

void Init_fds()
{
	int i=0;
	for(;i<MAX;i++)
	{
		fds[i]=-1;
	}
}

int Add_fds(int c)
{
	pthread_mutex_lock(&mutex);
	int i=0;
	for(;i<MAX;i++)
	{
		if(fds[i]==-1)
		{
			fds[i]=c;
			pthread_mutex_unlock(&mutex);
			return 1;
		}
	}
	if(i==MAX)
	{
		pthread_mutex_unlock(&mutex);
		return 0;
	}
}

void Del_fds(int i)
{
	for(;i<MAX-1;i++)
	{
		fds[i]=fds[i+1];

		if(fds[i+1]==-1)
		{
			break;
		}
	}
	fds[MAX-1]=-1;
}

int Get_fds()
{
	pthread_mutex_lock(&mutex);
	int i=0,c=-1;
	for(;i<MAX;i++)
	{
		if(fds[i]!=-1)
		{
			c=fds[i];
			Del_fds(i);
			break;
		}
	}
	pthread_mutex_unlock(&mutex);
	return c;
}

void *pthread_fun(void *arg)
{
	while(1)
	{
		sem_wait(&sem);
		int c=Get_fds();
		if(c==-1)
		{
			printf("Not Found\n");
			continue;
		}
		while(1)
		{
			char buff[128]={0};
			int n=recv(c,buff,127,0);
			if(n<=0)
			{
				close(c);
				break;
			}
			printf("buff::%s\n",buff);
			send(c,"OK",2,0);
		}
	}
}

int main()
{
	Init_fds();
	sem_init(&sem,0,0);
	pthread_mutex_init(&mutex,NULL);
	int i=0;
	for(;i<3;i++)
	{
		pthread_t id;
		int res=pthread_create(&id,NULL,pthread_fun,NULL);
		assert(res==0);
	}

	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);

	struct sockaddr_in ser,cli;
	memset(&ser,0,sizeof(ser));
	ser.sin_family=AF_INET;
	ser.sin_port=htons(6000);
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");

	int res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	assert(res!=-1);

	listen(sockfd,5);

	while(1)
	{
		socklen_t len=sizeof(cli);
		int c=accept(sockfd,(struct sockaddr*)&cli,&len);
		if(c<0)
		{
			printf("errror\n");
			continue;
		}

		if(!Add_fds(c))
		
		{
			send(c,"please wait...",strlen("please wait..."),0);
			close(c);
			continue;
		}
		sem_post(&sem);
	}
	return 0;
}
客户端代码不变。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值