本文摘自陈硕老师的linux多线程服务端编程
1. 单线程服务器的常用编程模型
non-blocking + IO multiplexing 模型,即Reactor 模型。
Reactor模型是事件驱动模型,有一个或者多个并发输入源,类似于生产者与消费者模式,有一个或者多个生产者将事件放入一个Queue中,而一个或者多个消费者主动从队列中Poll事件来处理。
Reactor基于事件回调函数必须是非阻塞的,否则会影响其他连接的正常运行。
举例来说:
如果事件发生,socket可读,但是协议栈检查到这个新分节检验和错误,然后丢弃这个分节,这时候调用read则无数据可读,如果是阻塞,则会阻塞在read。
2. 多线程服务器常用的编程模型
one loop per thread + thread pool 这样的模式需要一个优质的基于Reactor的库来支持。
-
one loop per thread
程序里的每一个IO线程都有一个Event Loop(non-blocking + IO multiplexing)或者叫 Reactor -
线程池
是一种多线程处理形式,处理过程将任务添加到队列,然后创建线程启动任务。然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。
3. 多线程服务器适用的场合
选择 运行多个单线程的进程 这种模式
- 必须用单线程的场合
程序中可能会用到fork,只有单线程才可以fork
优点:能避免过分抢夺系统的计算资源
缺点:无法设置优先级
- 适用多线程的场景
- 共享数据可以修改
- 提供非均质的服务(优先级差异)
4. 线程分类
一个多线程服务程序中的线程大致可以分为3类:
- IO线程
这类线程的主循环是IO multiplexing,阻塞地等待select/poll/epoll_wait系统调用上 - 计算线程
- 第三方库所用的线程
5. 例释
- Linux能同时启动多少个线程?
与进程空间与线程默认栈大小有关。 - 多线程能提高并发量吗?
多线程不能提高并发连接数;
(1) 如果采用“一个线程一个连接”,则并发连接数与上面的同时启动线程数相关,很有限。
(2) 如果采用“one loop per thread” ,单个event loop 处理一万个长连接并不罕见。(主线程只要监听连接的到来,之后将连接的建立分配给回调函数来处理) - 多线程能提高吞吐量吗?
对于计算密集型服务,多线程不能提高吞吐量。 - 多线程程序如何让IO和计算相互重叠,降低延迟?
基本思路是将IO操作(通常是读写从左)通过 BlockingQueue 交给别的线程去处理。
总结
- 服务端编程,如果工作集(服务程序响应一次请求所访问的内存大小)较大,那么就用一个多线程的进程,避免CPU cache换入换出,影响性能;否则就用单线程多进程,享受单线程编程的便利。
- 线程不能减少工作量也不能减少CPU时间。
- 多线程服务端编程建议使用Reactor + Thread pool 模型,采取one thread per thread + Non-blocking模式。
- 将IO 线程与计算线程分开,需要“计算”的时间被放入BlockingQueue中,并用线程池来处理“计算”。