在学习muduo网络库前,应该先熟悉一下多线程网络服务编程模型。在6.6.2节介绍了11种方案。方案0到方案4用的是阻塞I/O。方案5到方案11用的都是阻塞I/O。
方案0: accept+read/write
方案0不是并发模型,只是一个循环处理。用代码表示的话,可以表示为:
while(true)
{
int fd=accept(……);
read(fd,……) or write(fd……);
close(fd);
}
一次只能处理一个连接,第一个连接处理完毕后,才可以进入下一次循环,否则阻塞在I/O的read或write上。
方案1 accept+fork
这是个并发模型,这个模型比较简单,在accept后,fork一个子进程,在子进程处理连接。可以表示为:
while(true)
{
int fd=accept(linstenFd,……);
int pid=fork();
if(pid==0)//child
{
close(listenFd);
read/write(fd……);
close(fd);
}
//Parent
close(fd);
}
这个模型中要注意的是,要在子进程关闭监听的fd,在父进程关闭到来连接的fd。
方案2 accept+thread
这个方案和方案1类似,只是这个方案中是通过新建了线程来处理连接,方案1是通过新建线程来处理连接。
void ProcessIO(void* arg)
{
int fd=*static_cast<int *>arg;
read/wire(fd,……);
close(fd);
}
void ProcessAccept()
{
while(true)
{
int fd=accept(……);
pthread_create(……, NULL, ProcessIO, &fd);
}
}
}
先调用ProcessAccept等待连接,如果有连接,则创建新线程来调用ProcessIO,把新建连接的fd传给这个函数。
方案3 prefork
这个和方案1类似,只是先创建好进程。当有连接到来时,可以马上使用这些进程。具体可以参考http://www.t086.com/code/apache2.2/mod/prefork.html
方案4 pre thread
这和方案2类似,先创建好线程,等连接到来时,省去了创建线程的开销。
方案5 poll(reactor)
这个方案是基于I/O复用的select/poll/epoll;复用的是进程,不是I/O。这是一个单线程/进程的方案,在I/O事件到达后,直接在当前线程/进程处理I/O。单线程/进程的Reactor模式,在处理当前I/O事件时,如果有新的I/O事件到来,不能及时响应。这样事件的优先级不能得到保证。
方案6 reactor+thread-per-task
这个方案是为每一个I/O事件创建一个线程,在新建的线程中处理I/O事件。注意,这里是为每个I/O事件创建一个线程,而不是为每个连接创建线程。这样一来,每个新建线程处理的I/O事件的结果会有out-of-order的可能,即多次处理后的顺序和请求顺序未必一直了。
方案7 reactor+worker thread
为了避免方案6中的out-of-order的问题,在这个方案中,为每个连接创建一个线程。但是线程数目受限于CPU。
方案8 reactor+thread pool
这个方案是,在reactor线程中,等待I/O事件,当I/O事件到来时,在thread pool中取出一个线程(不是新创建)来处理I/O事件。
方案9 reactors in threads
muduo和Netty采用的是这种方案。在一个main Reactor中负责accept,之后把建立的连接fd放到sub Reactor中,这个连接的所有操作都在sub Reactor中完成。这个方案的特点是one loop per thread,有多个thread。
方案10 reactors in process
这个是Nginx的方案,连接之间无交换时,这是很好的解决方案。
方案11 reactors+thread pool
这是方案8和方案9的混合体。即使用多个Reactor,有的负责accept,有的负责I/O事件的到来。再使用线程池,处理I/O事件。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/kangroger/article/details/47048215