相关网络编程函数:http://blog.csdn.net/somehow1002/article/details/72648743
预分配线程(prethreading)是让服务器在启动阶段创建一个线程池,每个客户请求由当前可用线程池中的某个闲置线程处理
1.初始版本:每个客户一个线程
int main(int argc,char **argv){
int listenfd,connfd;
void sig_int(int);
void *doit(void *);
pthread_t tid;
socklen_t clilen,addrlen;
struct sockaddr *cliaddr;
if(argc==2)
listenfd=Tcp_listen(NULL,argv[1],&addrlen);
else if(argc==3)
listenfd=Tcp_listen(argv[1],argv[2],&addrlen);
else
err_quit("usage:server [<host>] <port#>");
cliaddr=malloc(addrlen);
signal(SIGNINT,sig_int);
for(;;){
chilen=addrlen;
connfd=accept(listenfd,cliaddr,&clilen);
pthread_create(&tid,NULL,&doit,(void *)connfd);
}
}
void *doit(void *arg){
void str_echo(int);
pthread_detach(pthread_self());
str_echo((int)arg);
close((int)arg);
return NULL;
}
上一个版本是来一个客户创建一个线程,但是很明显,预先派生一个子线程池比现场创建线程性能更加高效。
预先创建线程池程序
typedef struct{
pthread_t thread_tid;
long thread_count;
}Thread;
Thread *tptr;
int listenfd,nthreads;
socklen_t addrlen;
pthread_mutex_t mlock=PTHREAD_MUTEX_INITALIZER;
int main(int argc,char **argv){
int i;
void sig_int(int),thread_make(int);
if(argc==3)
listenfd=Tcp_listen(NULL,argv[1],&addrlen);
else if(argc==4)
listenfd=Tcp_listen(argv[1],argv[2],&addrlen);
else
err_quit("usage:server [<host>] <port#> <#threads>");
nthreads=atoi(argv[argc-1]);
tptr=calloc(nthreads,sizeof(Thread));
for(i=0;i<nthreads;i++)
thread_make(i);
signal(SIGINT,sig_int);
for(;;)
pause();
}
void thread_make(int i){
void *thread_main(void *);
pthread_create(&tptr[i].thread_tid,NULL,&thread_main,(void *)i);
return;
}
void * thread_main(void *arg){
int connfd;
void str_echo(int);
socklen_t clilen;
struct sockaddr *cliaddr;
cliaddr=malloc(addrlen);
printf("thread %d starting\n",(int)arg);
for(;;){
clilen=addrlen;
//accept进行了加锁保护
pthread_mutex_lock(&mlock);
connfd=accept(listenfd,cliaddr,&clilen);
pthread_mutex_unlock(&mlock);
tptr[(int)arg].thread_count++;
str_echo(connfd);
close(connfd);
}
}
3.主线程统一accept
类似于进程的描述符传递版本,这一个版本的程序让主线程调用accept并吧每个客户端连接传递给线程池中某个可用线程。
不同于进程版本的是,描述符的传递并不需要使用unix域协议。既然所有线程和套接字都在同一个进程内,那么我们就没有必要把一个描述符从一个线程传递到另一个线程。这个版本的实现是通过一个全局数组clifd。主线程将描述符存入,然后线程池中某个线程取出描述符。clifd数组的维护是通过互斥锁和条件变量实现的。
#define MAXNCLI 32
int clifd[MAXNCLI],iget,iput;
pthread_mutex_t clifd_mutex=PTHREAD_MUTEX_INITALIZER;
pthread_cond_t clifd_cond=PTHREAD_MUTEX_INITALIZER;
static int nthreads;
int main(int argc,char **argv){
int i,listenfd,connfd;
void sig_int(int),thread_make(int);
socklen_t addrlen,clilen;
struct sockaddr *cliaddr;
if(argc==3)
listenfd=Tcp_listen(NULL,argv[1],&addrlen);
else if(argc==4)
listenfd=Tcp_listen(argv[1],argv[2],&addrlen);
else
err_quit("usage:server [<host>] <port#> <#threads>");
cliaddr=malloc(addrlen);
nthreads=atoi(argv[argc-1]);
tptr=calloc(nthreads,sizeof(Thread));
iget=iput=0;
for(i=0;i<nthreads;i++)
thread_make(i);
signal(SIGINT,sig_int);
for(;;){
clilen=addrlen;
connfd=accept(listenfd,cliaddr,&clilen);
pthread_mutex_lock(&clifd_mutex);
clifd[iput]=connfd;
if(++iput==MAXNCLI)
iput=0;
if(iput==iget)
err_quit("iput=iget=%d",iput);
pthread_cond_signal(&clifd_cond);
pthread_mutex_unlock(&clifd_mutex);
}
}
void thread_make(int i){
void *thread_main(void *);
pthread_create(&tptr[i].thread_tid,NULL,&thread_main,(void *)i);
return ;
}
void *thread_main(void *arg){
int connfd;
void str_ehco(int);
printf("thread %d starting\n",(int)arg);
for(;;){
pthread_mutex_lock(&clifd_mutex);
while(iget==iput)
pthread_cond_wait(&clifd_cond,&clifd_mutex);
connfd=clifd[iget];
if(++iget==MAXNCLI)
iget=0;
pthread_mutex_unlock(&clifd_mutex);
tptr[(int)arg].thread_count++;
str_echo(connfd);
close(connfd);
}
}