《Linux多线程服务器端编程》第3章主要讲的是多线程服务器的适用场合与常用的编程模型。
1.进程和线程
一个进程是"内存中正在运行的程序“。每个进程都有自己独立的地址空间(address space)。将"进程"比喻为"人",每个人都有自己的记忆(memory),人与人通过谈话(消息传递)来交流,谈话既可以是面谈(同一台服务器),也可以在电话里谈(不同的服务器,有网络通信)。面谈和电话谈的区别在于,面谈可以立即知道对方是否死了(crash,SIGCHILD),而电话谈只能通过周期性的心跳来判断对方是否还活着。
线程的特点是共享进程地址空间,从而可以高效地共享数据。多线程的价值,可以更好地发挥多核处理器(multi-cores)的效能。
2.单线程服务器的常用模型
在高性能的网络程序中,使用最为广泛的恐怕要数 "non-blocking IO + IO multiplexing"这种模型,既Reactor模式。
程序的基本结构是一个事件循环(event loop),以事件驱动(event-driven)和事件回调的方式实现业务逻辑:
//代码仅为示意,没有完整考虑各种情况 while(!done){ int timeout_ms=max(1000,getNextTimedCallback()); int retval=::poll(fds,nfds,timeout_ms); if(retval<0){ 处理错误,回调用户的error handler } else{ 处理到期的timers,回调用户的timer handler if(retval>0){ 处理IO事件,回调用户的IO event handler } } }
Reactor模式的优点明显,不仅可以用于读写socket,连接的建立(connect/accept)甚至DNS解析都可以用非阻塞地方式进行,以提高并发度和吞吐量,对于IO密集的应用是个不错的选择。但基于事件驱动的编程模型也有其本质的缺点,它要求事件回调函数必须是非阻塞的。对于涉及网络IO的请求响应式协议,它容易割裂业务逻辑,使其散布于多个回调函数之中,相对不容易理解和维护。
3.多线程服务器的常用编程模型
1.每个请求创建一个线程,使用阻塞式IO操作。
2.使用线程池,同样使用阻塞式IO操作。与第一种相比,这是提高性能的措施.
3.使用non-blocking IO + IO multiplexing.
4. Leader/Follwer等高级模式。
4.推荐模式
总结起来,推荐的C++多线程服务器端编程模式为:one(event) loop per thread + thread pool
服务器开发:跑在多核机器上的Linux用户态的没有用户界面的长期运行的网络应用程序,通常是分布式系统的组成部件。