对于“进程仍是线程?”这个问题,也常常困扰着那些进行软件架构设计的家伙。因此今天打算聊一下我对这个问题的体会。假如你还搞不清楚线程和进程的区别,请先找本操做系统原理的书好好拜读一下,再回来看帖。程序员
因为这个问题很容易引起口水战,事先声明以下:多进程和多线程,没法一律而论地说谁比谁好。所以本帖主要描述特定场景(与我所负责的产品相关)下,进程和线程的权衡经验,仅供大伙儿参考。数据库
因为特定场景是本帖讨论的前提,先说说我目前负责的产品的特色:业务逻辑比较复杂、业务数据量比较大、对数据实时处理的性能要求比较高、对健壮性和安全性要求比较高、要求跨平台(包括操做系统、数据库)、某些状况下须要分布部署。编程
上面说了一大堆,其实有很多的应用系统符合上述特色,好比:某些网络游戏服务器、某些金融行业的业务系统、某些电子商务的交易系统等等。若是你正在从事的是相似的应用系统的设计,但愿我下面介绍的经验对你有帮助。 安全
★进程【颗粒度】问题 服务器
大伙儿应该明白,进程和线程都是处理并发(concurrency)的手段。对于上述这种比较复杂的系统,若是你企图所有用进程(见“注1”)或者所有用线程(见“注2”)来处理并发,估计会死得很难看。因此,关键问题就是如何在进程和线程之间进行平衡(也就是肯定进程颗粒度的问题)。注1网络
所谓“所有用进程”,就是全部的并发都使用进程实现(所以每一个进程只有一个线程)。这种设计在某些平台上(好比 Windows)会致使严重的性能问题。
注2多线程
所谓“所有用线程”,就是全部的并发都使用线程实现(所以整个系统只有一个进程)。这种设计的健壮性极差(一个致命错会致使整个系统崩溃),并且更别提分布部署了。
我我的建议,尽可能以业务逻辑的单元来划分进程。这样作的好处有以下几点:架构
★“以业务逻辑为单元”划分进程的好处并发
◇避免扯皮
通常来讲,某个固定业务逻辑的开发人员也是相对固定的。若是业务逻辑对应的某个进程崩溃了,测试人员容易快速定位肇事者,而后直接提交Bug给他/她。反之,一个进程搞得太庞大,N 多人掺和在里面,一旦进程崩溃了,相关编程人员之间很容易互相扯皮,不利于维护安定团结的局面;另外,因为测试人员常常搞不清楚 Bug 属于谁,常常给错 Bug,也容易制造人民内部矛盾。由此能够看出,【相对细】的进程颗粒度可以避免一些管理上的麻烦。因为 XXX 常常教导咱们:“稳定压倒一切”,因此该优势列第一条。
◇健壮性、容错性 编程语言
通常来讲,开发人员的水平良莠不齐,优秀的毕竟是少数(具体参见“二八原理系列”的帖子)。因此不免会有菜鸟程序员搞出低级错误,而有些低级错误是致命的,会致使进程的崩溃。若是你是以业务逻辑划分进程,一个业务逻辑的进程崩溃,对其它业务逻辑的影响不大(除非是该业务逻辑的依赖方);所以就不会出现“所有用线程”致使的弊端。
◇分布式
我常遇见的分布式部署需求,通常都是按照业务逻辑的维度来划分。好比系统中有一个认证模块,里面包含有敏感的用户认证信息。这时候客户就会要求把该模块单独部署在一台通过安全加固的主机中(以防阶级敌人搞破坏)。若是是以业务逻辑为单位划分进程,要知足上述的部署需求就相对容易了(只要再配合恰当的进程间通信机制,下面会提到)。另外,支持分布式部署还能够顺带解决性能问题。好比某个业务逻辑模块特别消耗硬件资源(好比:内存、CPU、硬盘、带宽),就能够把它拿出去单独放一台机器上跑。
◇跨编程语言
这个好处可能不少人容易忽略。通常来讲,每一个编程语言都有各自的优缺点。若是你经过业务逻辑划分进程,就能够根据不一样的业务逻辑的特色来选择合适的编程语言。
★进程间通信(如下简称 IPC)问题
既然不可能把整个系统放入一个进程,那就必然会碰到 IPC 的问题。下面就来讲一下该如何选择 IPC。
各类操做系统里面,有不少稀奇古怪的 IPC 类型。因为要考虑跨平台,首先砍掉一批。剩下的 IPC 类型中,可以进行数据传输的 IPC 就很少了,主要有以下几种:套接字(如下简称 Socket)、共享内存、管道、文件。
其中 Socket 是俺强烈推荐的 IPC 方式,理由以下:使用 Socket 能够自然地支持分布式部署;使用 Socket 能够比较容易地实现多种编程语言的混合;使用 Socket 还能够省掉了一大坨“锁操做”的代码。
列位看官中,或许有人在担忧 Socket 的性能问题,其实大可没必要多虑。当两个进程在【本机】上进行 Socket 通信时,因为可使用 localhost 环回地址,数据不用通过物理网卡,操做系统内核还能够进行某些优化。这种状况下,Socket 相对其它几种IPC机制,不会有太大的性能误差。
最后再补充一下,Socket 方式也能够有效防止扯皮问题。举个例子:张三写了一个进程 A,李四写了一个进程 B,进程 A 经过 Socket 方式发数据给进程B。忽然有一天,两个进程的通信出故障了。而后张三就说是李四接收数据出错;李四就说张三发送数据出错。这时候怎么办捏?很简单,随便找个 Sniffer 软件当场抓一下数据包并 Dump 出来看,问题就水落石出了。
★为啥还要线程?
上面说了这么多进程的好处,有同窗要问了:“那线程有什么用捏?”总的来讲,使用线程出于两方面的考虑:性能因素和编码方便。
◇性能因素
因为某些操做系统(好比 Windows)中的进程比较重型,若是【频繁】建立进程或者建立大量进程,会致使操做系统的负载太高。举例以下:
假设你要开发一个相似 Web Server 的应用。你针对每个客户端请求建立一个对应的进程用于进行数据交互(是否是想起了古老的 CGI)。一旦这个系统扩容,用户的并发链接数一增长,你的应用立马死翘翘。
上面的例子代表,跨平台软件系统的进程数要保持相对稳定。若是你的进程数会随着某些环境因素呈线性增加,那就至关不妙了(顺带说一下,若是线程数会随着环境因素呈线性增加,也至关不妙)。而根据业务逻辑的单元划分进程,顺便能达到“进程数的相对稳定”的效果。
◇编码方面
因为业务逻辑内部的数据耦合比较紧密。若是业务逻辑内部的并发也用进程来实现,可能会致使大量的 IPC 编码(任意两个进程之间只要有数据交互,就得写一坨 IPC 代码)。这或许会让相关的编程人员怨声载道。固然,编码方面的问题也不是绝对的。假如你的系统有很成熟且方便易用的IPC库,能够比较透明地封装IPC相关操做,那这方面的问题也就不存在了。