基于预线程化的并发服务器

来源:互联网 发布:淘宝永久封店保证金 编辑:程序博客网 时间:2024/06/08 15:36

           本文摘录自 深入理解计算机系统 :需要消费者-生产者模型的基本知识 ,和网络编程的基础知识

       消费者-生产者模型:http://blog.csdn.net/lujiandong1/article/details/45502165

       网络编程:http://blog.csdn.net/lujiandong1/article/details/45506543

      预线程化的并发服务器模型如下:

       

       在一般的并发服务器中,我们为每一个新客户端创建了一个新线程,每来一个请求,我们就创建一个线程,导致不小的代价。一个基于预线程化的服务器通过生产者-消费者 模型来降低开销。服务器由一个主线程和一组工作线程构成。主线程不断接受来自客户端的连接请求,并将得到的连接描述符放在一个有限的缓冲区中。每一个工作者线程反复从共享缓冲区取出描述符,为客户端服务,等待下一个描述符。

       总结:提前创建了几个工作者线程,相当于构建一个线程池,工作者线程不断运行,只要缓冲区里面有连接描述符,就取出连接描述符,并进行服务,服务完之后,工作者线程不会取消,还是在等缓冲区里面产生新的连接描述符。

       贴上代码:代码也只能在linux平台下运行,在windows下跑不了

//客户端程序在http://blog.csdn.net/lujiandong1/article/details/45506543中已经讲解了。

客户端程序 echoclient.c---------------------------------------------------------------------------------------------------------------------------------------------

/* * echoclient.c - An echo client *//* $begin echoclientmain */#include "csapp.h"int main(int argc, char **argv) {    int clientfd, port;    char *host, buf[MAXLINE];    rio_t rio;    if (argc != 3) {fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);exit(0);    }    host = argv[1];    port = atoi(argv[2]);    clientfd = Open_clientfd(host, port);    Rio_readinitb(&rio, clientfd);    while (Fgets(buf, MAXLINE, stdin) != NULL) {Rio_writen(clientfd, buf, strlen(buf));Rio_readlineb(&rio, buf, MAXLINE);Fputs(buf, stdout);    }    Close(clientfd); //line:netp:echoclient:close    exit(0);}/* $end echoclientmain */
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

服务器程序--------------------------------------------------------------------------------------------------------------------------------------------------------------

/*  * echoservert_pre.c - A prethreaded concurrent echo server *//* $begin echoservertpremain */#include "csapp.h"#include "sbuf.h"#define NTHREADS  4#define SBUFSIZE  16 void echo_cnt(int connfd);void *thread(void *vargp);sbuf_t sbuf; /* shared buffer of connected descriptors */int main(int argc, char **argv) {    int i, listenfd, connfd, port;    socklen_t clientlen=sizeof(struct sockaddr_in);    struct sockaddr_in clientaddr;    pthread_t tid;     if (argc != 2) {fprintf(stderr, "usage: %s <port>\n", argv[0]);exit(0);    }    port = atoi(argv[1]);    sbuf_init(&sbuf, SBUFSIZE); //line:conc:pre:initsbuf    listenfd = Open_listenfd(port);    for (i = 0; i < NTHREADS; i++)  /* Create worker threads */ //line:conc:pre:begincreatePthread_create(&tid, NULL, thread, NULL);               //line:conc:pre:endcreate    while (1) { connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);sbuf_insert(&sbuf, connfd); /* Insert connfd in buffer */    }}void *thread(void *vargp) {      Pthread_detach(pthread_self());     while (1) { int connfd = sbuf_remove(&sbuf); /* Remove connfd from buffer */ //line:conc:pre:removeconnfdecho_cnt(connfd);                /* Service client */Close(connfd);    }}/* $end echoservertpremain */

解释:1、sbuf_init ,sbuf_insert等都来自开发的基于生产者-消费者模型的SBUF包,见博客

                 http://blog.csdn.net/lujiandong1/article/details/45502165

           2、创建了NTHREADS个工作这线程

    for (i = 0; i < NTHREADS; i++)  /* Create worker threads */ //line:conc:pre:begincreatePthread_create(&tid, NULL, thread, NULL);               //line:conc:pre:endcreate
           3、服务器主线程进入无限循环,不断监听连接请求,只要客户端有请求到达,就创建一个已连接描述符,插入缓冲区。

           4、每个工作者线程的行为也比较简单,从已连接描述符的缓冲区里面取一个已连接描述符,然后调用echo_cnt为客户端服务。echo_cnt只是回送客户端的输入。

           5、使用Pthread_detach(pthread_self())分离线程,当线程结束时,线程的存储器资源自动被系统回收

/*  * A thread-safe version of echo that counts the total number * of bytes received from clients. *//* $begin echo_cnt */#include "csapp.h"static int byte_cnt;  /* byte counter */static sem_t mutex;   /* and the mutex that protects it */static void init_echo_cnt(void){    Sem_init(&mutex, 0, 1);    byte_cnt = 0;}void echo_cnt(int connfd) {    int n;     char buf[MAXLINE];     rio_t rio;    static pthread_once_t once = PTHREAD_ONCE_INIT;    Pthread_once(&once, init_echo_cnt); //line:conc:pre:pthreadonce    Rio_readinitb(&rio, connfd);        //line:conc:pre:rioinitb    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {P(&mutex);byte_cnt += n; //line:conc:pre:cntaccess1printf("thread %d received %d (%d total) bytes on fd %d\n",        (int) pthread_self(), n, byte_cnt, connfd); //line:conc:pre:cntaccess2V(&mutex);Rio_writen(connfd, buf, n);    }}/* $end echo_cnt */

解释:

        1、对共享变量byte_cnt的访问,要保证互斥访问,使用P和V操作。

        2、全局变量byte_cnt中记录了从客户端接收到的累计字节数

        3、要初始化化byte_cnt计数器和mutex信号量,可以提供一个初始化函数,然后在例程函数中去调用Pthread_once.


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------





0 0
原创粉丝点击