1、accept + fork, TCP并发服务器程序
每个客户一个子进程
存在的问题是每个客户现场fork 一个子进程比较悍匪CPU时间。
此类型并发服务器的主体循环伪代码如下
for(;;){
clilen = addrlen;
if((confd = accept(listenfd,cliaddr,&clilem)) < 0)
{
if(errno == EINTR)
continue;
else
err_sys("accept error");
}
if((childpid = fork()) == 0)
{
close(listenfd);//关闭子进程的监听描述符
web_child(confd);//处理请求
exit(0);
}
close(confd);
}
2、accept + thread, TCP并发服务器程序
每个客户一个线程
此类型并发服务器的主体循环伪代码如下
pthread_t tid;
for(;;){
clilen = addrlen;
if((confd = accept(listenfd,cliaddr,&clilem)) < 0)
{
if(errno == EINTR)
continue;
else
err_sys("accept error");
}
pthread_create(&tid,NULL,&doit,(void *)confd);
}
void * doit(void * arg)
{
void web_thread(int);
pthread_detach(pthread_self());
web_thread((int)arg);
close((int)arg);
return(NULL);
pthread_detach 函数将子线程与主线程分离,使得主线程不必等待子线程。
3、线程池, TCP并发服务器程序
在程序启动之后只让主线程调用accept,并把每个客户连接传递给池中的某个可用线程。
问题在于如何将一个已连接套接字传递给线程池中的某个可用的线程。我们原本可以使用描述符传递,但是既然所有的描述符和所有线程都在一个进程内,没有必要将一个描述符从一个线程传递到另外一个线程。接收线程只需要主动这个已连接套接字描述符的值,而描述符传递实际传递的并非是这值,而是这个套接字的一个引用,因而将返回一个不同于原值的描述符(但是套接字的引用计数增加)。
此类型并发服务器的主体循环伪代码如下
int nthread;
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITTIALIZER;
/
创建线程池中的每一个线程
void thread_make(int i)
{
void *thread_main(void*);
pthread_create(&tptr[i].thread_tid,NULL,&thread_main,(void *)i);
return;//主线程返回
}
//线程池中的每个线程都试图获取保护clifd数组的互斥锁,获得之后测试input和iget,若两者相等,表示无事可做,
//于是通过pthread_cond_wait睡眠在条件变量上。主线程接收一个连接之后会调用pthread_cond_signal 向条件变量发送信号,
//以唤醒睡眠在其上的线程。若input与iget不相等,则从clifd数组中取出下一个元素获取一个连接,然后调用web_child
void thread_main(void * arg)
{
int confd;
void web_thread(int);
printf("thread %d strating\n",(int)arg);
for(;;)
{
pthread_mutex_lock(&clifd_mutex);
while(iget == input)
pthread_cond_wait(&clifd_cond,&clifd_mutex);
confd = clifd[iget];
if(++iget == MAXNCLI)
iget = 0;
pthread_mutex_unlock(&clifd_mutex);
tptr[(int)arg].thread_count++;
web_child(confd);
close(confd);
}
}
//
for(i = 0;i<nthread;++i)
thread_make(i);//只有主线程返回,创建线程池中的每一个线程
for(;;){
clilen = addrlen;
if((confd = accept(listenfd,cliaddr,&clilem)) < 0)
{
if(errno == EINTR)
continue;
else
err_sys("accept error");
}
pthread_mutex_lock(&clifd_mutex);
clifd[input] = confd;//clifd[]数组,由主线程往中存入已接受的已连接套接字描述符,
//并由线程池中的可用线程从中取出一个来服务相应的客户
if(++input == MAXNCLI) //input 是主线程将往该数组clifd[]中存入的下一个元素的下标
input = 0;
if(input == iget) //iget表示线程池中某个线程将从该数组clifd[]中取出的下一个元素的下标。
err_quit("input = iget = %d",input);
pthread_cond_signal(&clifd_cond);\
pthread_mutex_unlock(&clifd_mutex);
}
4、Reactor + thread-per-task, TCP并发服务器程序
服务器收到请求之后,创建一个新的线程取处理请求,以充分利用多核CPU。
5、Reactors in threads, TCP并发服务器程序
此方案的特定是one loop per thread, 有一个main reactor 负责accept 连接,然后将连接挂在某个sub reactor 中,这样该连接的所有操作都在那个 sub reactor 所处的线程中完成。
多个连接可能被分配到多个线程,充分利用CPU。
分类 网络编程 学习笔记