陈硕 (giantchen_AT_gmail)
Blog.csdn.net/Solstice
2010 Feb 28
这篇文章原本是前一篇博客《多线程服务器的常用编程模型》(以下简称《常用模型》)计划中的一节,今天终于写完了。
“服务器开发”包罗万象,本文所指的“服务器开发”的含义请见《常用模型》一文,一句话形容是:跑在多核机器上的 Linux 用户态的没有用户界面的长期运行的网络应用程序。“长期运行”的意思不是指程序 7x24 不重启,而是程序不会因为无事可做而退出,它会等着下一个请求的到来。例如 wget 不是长期运行的,httpd 是长期运行的。
正名
与前文相同,本文的“进程”指的是 fork() 系统调用的产物。“线程”指的是 pthread_create() 的产物,而且我指的 pthreads 是 NPTL 的,每个线程由 clone() 产生,对应一个内核的 task_struct。本文所用的开发语言是 C++,运行环境为 Linux。
首先,一个由多台机器组成的分布式系统必然是多进程的(字面意义上),因为进程不能跨 OS 边界。在这个前提下,我们把目光集中到一台机器,一台拥有至少 4 个核的普通服务器。如果要在一台多核机器上提供一种服务或执行一个任务,可用的模式有:
- 运行一个单线程的进程
- 运行一个多线程的进程
- 运行多个单线程的进程
- 运行多个多线程的进程
这些模式之间的比较已经是老生常谈,简单地总结:
- 模式 1 是不可伸缩的 (scalable),不能发挥多核机器的计算能力;
- 模式 3 是目前公认的主流模式。它有两种子模式:
- 3a 简单地把模式 1 中的进程运行多份,如果能用多个 tcp port 对外提供服务的话;
- 3b 主进程+woker进程,如果必须绑定到一个 tcp port,比如 httpd+fastcgi。
- 模式 2 是很多人鄙视的,认为多线程程序难写,而且不比模式 3 有什么优势;
- 模式 4 更是千夫所指,它不但没有结合 2 和 3 的优点,反而汇聚了二者的缺点。
本文主要想讨论的是模式 2 和模式 3b 的优劣,即:什么时候一个服务器程序应该是多线程的。
从功能上讲,没有什么是多线程能做到而单线程做不到的,反之亦然,都是状态机嘛(我很高兴看到反例)。从性能上讲,无论是 IO bound 还是 CPU bound 的服务,多线程都没有什么优势。那么究竟为什么要用多线程?
在回答这个问题之前,我先谈谈必须用必须用单线程的场合。
必须用单线程的场合
据我所知,有两种场合必须使用单线程:
- 程序可能会 fork()
- 限制程序的 CPU 占用率
先说 fork(),我在《<