13
14
15
16
17
18
bind(srvfd);
listen(srvfd);
for(;;){
clifd = accept();
pthread_create(...,thread_callback,&clifd);
}
服务端分为主线程和工作线程,主线程负责 accept()连接,而工作线程负责处理业务逻辑和
流的读取等。因此,即使在工作线程阻塞的情况下,也只是阻塞在线程范围内,对继续接
受新的客户端连接不会有影响。
第二种实现方式,通过线程池的引入可以避免频繁的创建、销毁线程,能在很大程序上提
升性能。但不管如何实现,多线程模型先天具有如下缺点:
1)稳定性相对较差。一个线程的崩溃会导致整个程序崩溃。
2)临界资源的访问控制,在加大程序复杂性的同时,锁机制的引入会是严重降低程序的性
能。性能上可能会出现“辛辛苦苦好几年,一夜回到解放前”的情况。
4.IO 多路复用模型之 select/poll
多进程模型和多线程(线程池)模型每个进程/线程只能处理一路 IO,在服务器并发数较高的
情况下,过多的进程/线程会使得服务器性能下降。而通过多路 IO 复用,能使得一个进程
同时处理多路 IO,提升服务器吞吐量。
在 Linux 支持 epoll 模型之前,都使用 select/poll 模型来实现 IO 多路复用。
以 select 为例,其核心代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bind(listenfd);
listen(listenfd);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for(;;){
select(...);
if (FD_ISSET(listenfd, &rset)) {/*有新的客户端连接到来*/
clifd = accept();
cliarray[] = clifd; /*保存新的连接套接字*/
FD_SET(clifd, &allset);/*将新的描述符加入监听数组中*/
}
for(;;){/*这个 for 循环用来检查所有已经连接的客户端是否由数据可读写*/
fd = cliarray[i];
if (FD_ISSET(fd , &rset))
dosomething();
}
}
select IO 多路复用同样存在一些缺点,罗列如下: