Welcome

首页 / 软件开发 / .NET编程技术 / 多线程服务器的常用编程模型

多线程服务器的常用编程模型2011-05-06 博客园 陈硕本文主要讲我个人在多线程开发方面的一些粗浅经验。总结了一两种常用的线程模型, 归纳了进程间通讯与线程同步的最佳实践,以期用简单规范的方式开发多线程程序。

文中的“多线程服务器”是指运行在 Linux 操作系统上的独占式网络应用程序。硬件平 台为 Intel x64 系列的多核 CPU,单路或双路 SMP 服务器(每台机器一共拥有四个核或八 个核,十几 GB 内存),机器之间用百兆或千兆以太网连接。这大概是目前民用 PC 服务器 的主流配置。

本文不涉及 Windows 系统,不涉及人机交互界面(无论命令行或图形);不考虑文件读 写(往磁盘写 log 除外),不考虑数据库操作,不考虑 Web 应用;不考虑低端的单核主机 或嵌入式系统,不考虑手持式设备,不考虑专门的网络设备,不考虑高端的 >=32 核 Unix 主机;只考虑 TCP,不考虑 UDP,也不考虑除了局域网络之外的其他数据收发方式( 例如串并口、USB口、数据采集板卡、实时控制等)。

有了以上这么多限制,那么我将要谈的“网络应用程序”的基本功能可以归纳为“收到 数据,算一算,再发出去”。在这个简化了的模型里,似乎看不出用多线程的必要,单线程 应该也能做得很好。“为什么需要写多线程程序”这个问题容易引发口水战,我放到另一篇 博客里讨论。请允许我先假定“多线程编程”这一背景。

“服务器”这个词有时指程序,有时指进程,有时指硬件(无论虚拟的或真实的),请 注意按上下文区分。另外,本文不考虑虚拟化的场景,当我说“两个进程不在同一台机器上 ”,指的是逻辑上不在同一个操作系统里运行,虽然物理上可能位于同一机器虚拟出来的两 台“虚拟机”上。

本文假定读者已经有多线程编程的知识与经验,这不是一篇入门教程。

本文承蒙 Milo Yip 先生审读,在此深表谢意。当然,文中任何错误责任均在我。

1 进程与线程

“进程/process”是操作里最重要的两个概念之一(另一个是文件),粗略地讲,一个 进程是“内存中正在运行的程序”。本文的进程指的是 Linux 操作系统通过 fork() 系统 调用产生的那个东西,或者 Windows 下 CreateProcess() 的产物,不是 Erlang 里的那种 轻量级进程。

每个进程有自己独立的地址空间 (address space),“在同一个进程”还是“不在同一 个进程”是系统功能划分的重要决策点。Erlang 书把“进程”比喻为“人”,我觉得十分 精当,为我们提供了一个思考的框架。

每个人有自己的记忆 (memory),人与人通过谈话(消息传递)来交流,谈话既可以是面 谈(同一台服务器),也可以在电话里谈(不同的服务器,有网络通信)。面谈和电话谈的 区别在于,面谈可以立即知道对方死否死了(crash, SIGCHLD),而电话谈只能通过周期性 的心跳来判断对方是否还活着。

有了这些比喻,设计分布式系统时可以采取“角色扮演”,团队里的几个人各自扮演一 个进程,人的角色由进程的代码决定(管登陆的、管消息分发的、管买卖的等等)。每个人 有自己的记忆,但不知道别人的记忆,要想知道别人的看法,只能通过交谈。(暂不考虑共 享内存这种 IPC。)然后就可以思考容错(万一有人突然死了)、扩容(新人中途加进来) 、负载均衡(把 a 的活儿挪給 b 做)、退休(a 要修复 bug,先别给他派新活儿,等他做 完手上的事情就把他重启)等等各种场景,十分便利。

“线程”这个概念大概是在 1993 年以后才慢慢流行起来的,距今不过十余年,比不得 有 40 年光辉历史的 Unix 操作系统。线程的出现给 Unix 添了不少乱,很多 C 库函数 (strtok(), ctime())不是线程安全的,需要重新定义;signal 的语意也大为复杂化。据 我所知,最早支持多线程编程的(民用)操作系统是 Solaris 2.2 和 Windows NT 3.1,它 们均发布于 1993 年。随后在 1995 年,POSIX threads 标准确立。

线程的特点是共享地址空间,从而可以高效地共享数据。一台机器上的多个进程能高效 地共享代码段(操作系统可以映射为同样的物理内存),但不能共享数据。如果多个进程大 量共享内存,等于是把多进程程序当成多线程来写,掩耳盗铃。

“多线程”的价值,我认为是为了更好地发挥对称多路处理 (SMP) 的效能。在 SMP 之 前,多线程没有多大价值。Alan Cox 说过 A computer is a state machine. Threads are for people who can"t program state machines. (计算机是一台状态机。线程是给那些 不能编写状态机程序的人准备的。)如果只有一个执行单元,一个 CPU,那么确实如 Alan Cox 所说,按状态机的思路去写程序是最高效的,这正好也是下一节展示的编程模型。