线程池

来源:互联网 发布:playclub数据 编辑:程序博客网 时间:2024/06/18 13:28

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

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

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

总的来说线程池就是在服务器运行初始时,创建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  10int 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 10int 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;}
客户端代码不变。



原创粉丝点击