多线程和多进程模型的选用

多线程和多进程模型的选用

这里的线程指通过linux的pthread_create而产生的原生线程,线程资源很宝贵,能被操作系统的任务调度器看见的(不是python gevent、go gorouine里的概念); 
我们讨论以下两种模型;

  1. 多进程单线程模型(以下简称为多进程);
  2. 单进程多线程模型(以下简称为多线程);

多进程模型

优点

编程相对容易;通常不需要考虑锁和同步资源的问题。 
更强的容错性:比起多线程的一个好处是一个进程崩溃了不会影响其他进程。 
有内核保证的隔离:数据和错误隔离。 
对于使用如C/C++这些语言编写的本地代码,错误隔离是非常有用的:采用多进程架构的程序一般可以做到一定程度的自恢复;(master守护进程监控所有worker进程,发现进程挂掉后将其重启)

多进程的案例

nginx主流的工作模式是多进程模式(也支持多线程模型) 
几乎所有的web server服务器服务都有多进程的,至少有一个守护进程配合一个worker进程,例如apached,httpd等等以d结尾的进程包括init.d本身就是0级总进程,所有你认知的进程都是它的子进程; 
chrome浏览器也是多进程方式。 
redis也可以归类到“多进程单线程”模型(平时工作是单个进程,涉及到耗时操作如持久化或aof重写时会用到多个进程)

多线程模型

优点

多线程优点:创建速度快,方便高效的数据共享 
共享数据:多线程间可以共享同一虚拟地址空间;多进程间的数据共享就需要用到共享内存、信号量等IPC技术;

较轻的上下文切换开销 - 不用切换地址空间,不用更改寄存器,不用刷新TLB。 
提供非均质的服务 
如果全都是计算任务,但每个任务的耗时不都为1s,而是1ms-1s之间波动;这样,多线程相比多进程的优势就体现出来,它能有效降低“简单任务被复杂任务压住”的概率;

适用的场景

1 线程间有数据共享,并且数据是需要修改的(不同任务间需要大量共享数据或频繁通信时); 
2 提供非均质的服务(有优先级任务处理)事件响应有优先级; 
3 单任务并行计算,在非CPU Bound的场景下提高响应速度,降低时延; 
4 与人有IO交互的应用,良好的用户体验(键盘鼠标的输入,立刻响应)

多线程案例

桌面软件,响应用户输入的是一个线程,后台程序处理是另外的线程; 
memcached

选用

单进程多线程和多进程单线程,2种模式如何取舍? 
进程线程间创建的开销不足作为选择的依据,因为一般我们都是使用线程池或者进程池,在系统启动时就创建了固定的线程或进程,不会频繁的创建和销毁;

首先,根据工作集(需要共享的内存)的大小来定;如果工作集较大,就用多线程,避免cpu cache频繁的换入换出;比如memcached缓存系统;

其次,选择的依据根据以上多线程适用的场景来对比自身的业务场景,是否有这样场景需求:数据共享、提供非均质的服务,单任务拆散并行化等; 
如果没有必要,或者多进程就可以很好的胜任,就多用多进程,享受单线程编程带来便利;

RCU的发明者,Paul McKenny 在《Is Parallel Programming Hard, And, If So, What Can You Do About It?》说过: 
能用多进程方便的解决问题的时候不要使用多线程。

参考

ref:《Linux多线程服务端编程:使用muduo网络库》 
ref:http://www.zhihu.com/question/19903801 
ref:https://computing.llnl.gov/tutorials/pthreads/#WhyPthreads

Posted by: 大CC | 10OCT,2015 

博客:blog.me115.com [订阅
Github:大CC

----------------------------------------------------------------------------------------------------------

浅谈多进程多线程的选择(转)

关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”,这句话应付考试基本上够了,但如果在工作中遇到类似的选择问题,那就没有这么简单了,选的不好,会让你深受其害。 

经常在网络上看到有的XDJM问“多进程好还是多线程好?”、“Linux下用多进程还是多线程?”等等期望一劳永逸的问题,我只能说:没有最好,只有更好。根据实际情况来判断,哪个更加合适就是哪个好。 

我们按照多个不同的维度,来看看多线程和多进程的对比(注:因为是感性的比较,因此都是相对的,不是说一个好得不得了,另外一个差的无法忍受)。 

对比维度

多进程

多线程

总结

数据共享、同步

数据共享复杂,需要用IPC;数据是分开的,同步简单

因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂

各有优势

内存、CPU

占用内存多,切换复杂,CPU利用率低

占用内存少,切换简单,CPU利用率高

线程占优

创建销毁、切换

创建销毁、切换复杂,速度慢

创建销毁、切换简单,速度很快

线程占优

编程、调试

编程简单,调试简单

编程复杂,调试复杂

进程占优

可靠性

进程间不会互相影响

一个线程挂掉将导致整个进程挂掉

进程占优

分布式

适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单

适应于多核分布式

进程占优

看起来比较简单,优势对比上是“线程 3.5 v 2.5 进程”,我们只管选线程就是了? 

呵呵,有这么简单我就不用在这里浪费口舌了,还是那句话,没有绝对的好与坏,只有哪个更加合适的问题。我们来看实际应用中究竟如何判断更加合适。

1)需要频繁创建销毁的优先用线程

原因请看上面的对比。

这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的

2)需要进行大量计算的优先使用线程

所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。

这种原则最常见的是图像处理、算法处理。

3)强相关的处理用线程,弱相关的处理用进程

什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。

一般的Server需 要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任 务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。

当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。

4)可能要扩展到多机分布的用进程,多核分布的用线程

原因请看上面对比。

5)都满足需求的情况下,用你最熟悉、最拿手的方式

至于“数据共享、同步”、“编程、调试”、“可靠性”这几个维度的所谓的“复杂、简单”应该怎么取舍,我只能说:没有明确的选择方法。但我可以告诉你一个选择原则:如果多进程和多线程都能够满足要求,那么选择你最熟悉、最拿手的那个。 

需要提醒的是:虽然我给了这么多的选择原则,但实际应用中基本上都是“进程+线程”的结合方式,千万不要真的陷入一种非此即彼的误区。

  

1、多进程与多线程的简单比较

  1. 多进程

优点:内存隔离,单个进程的异常不会导致整个应用的崩溃。方便测试,编程简单。

    缺点:进程间调用,通讯和切换均比多线程大,耗资源。

使用场所:目标子动能交互少,如果资源和性能许可,可以设计由多个子应用程序来组合完成目的。

  1. 多线程 

优点:提高系统的并行性,并且开销小。数据共享方便(不需要进程间的通信)

缺点:没有内存隔离,单个现成的崩溃会导致整个应用程序的退出,发生采内存等bug时,定位及其不方便。编程复杂;调试困难;线程执行的随机性可能导致逻辑混乱,甚至发生死锁现象;

使用场所:在存在大量IO,网络等耗时操作,或者需要和用户交互时,使用多线程有利于提高系统的并行性和用户界面快速响应从而提高友好性。

2、设计时应注意的事项

  1. 尽量避免长驻内存的进程,例如那些很少用到的功能,或周期性很长(10分钟以上),

把它们的功能提取出来,做成一个小的应用程序。需要的时候再把它们拉起来(如通过crontab配置,或直接system)。

  1. 把目标设计成子功能系统的组合可用提高重用的易用性和维护性。

把目标根据功能划分不同的子系统,子系统间遵循特定的协议(文本或XML),由通讯联系起来,协作完成目标。

典型的案例就是UNIX或LINUX的工具使用。如:$ cat veglist fruitlist | sort > clist,用cat打开文件,协议是字符流,通过管道(通讯手段)传给sort进行排序,把排序的结果流重定向到文件中。这种自由组合协作风格应用思想和编程思路也是吸引众多UNIX拥护者的原因之一吧。

     在软件设计中,选择多进程还是多线程还跟很多因素有关,例如对数据实时处理的性能要求、对健壮性和安全性要求、是否要求跨平台(包括操作系统和数据库)及是否需要分布部署。因此,在设计中应充分考虑各种因素以求做到最大利用系统资源。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

做unix上server程序实现时:到底是该选择多线程并发模型还是多线程并发模型呢?

想到这个问题是源于阅读scgi,nginx,memched的源码~如下:


scgi的实现是用的多进程,主进程负责监听socket连接请求,然后分发给各个子进程来处理。
nginx的实现是用的多进程,创建好子进程之后,各个子进程直接自己来监听socket连接请求并处理。
memched的实现则是用的多线程,主线程负责监听请求,然后分发给各个子线程来处理。

那这样的话:本质上是两个可选条件的组合
1.多线程 还是 多进程?
2.是master统一监听请求并分发给worker处理 还是 各个worker自己监听请求并处理?

其中第2点的区别产生源于:
是 先创建socket 再fork子进程 还是 先fork子进程再创建socket?
前者因为先创建的socket,所以fork出来的子进程自然也继承了这个东西,所以可以直接用它来监听请求。 后者则不行···

================================================
先说第2点的区别吧:
我觉得如果说:让各个worker自己来监听请求的话:因为其监听的都是同一个socket,所以会产生竞争!到底谁先accept? 所以在实现上也搞个accept锁,让各个进程来争夺这个。否则来一个请求,所有子进程都呗warkup的话···惊群现象发生了·······

那master来统一监听并分发呢?嗯,master得知道各个worker是否就绪可用··怎么知道?好吧··各种IPC进程间通信····貌似也要有不小的开销···

======================================================

那和2点都该怎样来抉择 或者说 都有什么优缺点 适用用什么场景呢?


问题一. 选择多线程 还是 多进程

我理解这个问题,其实本质上就一句话:多进程之间各自有用自己的内存空间,而同一个进程内的多线程之间共享内存空间。

首先:server程序的本质是:client来获取和修改数据. 那么:不管是nginx还是memcached都是为了解决这个问题而存在的。

对于memcached而言(单进程多线程):这个进程启动后,用户写入这个cache的所有内容都是写在这个进程的堆空间中。 也就是说:所有的数据都是集中存在于唯一一个进程空间中,这样各个线程完全可以共享这些数据,可以全部的去查询和修改; 而如果我们换用多进程来实现的话,那用户传入cache的数据都存在哪?传过来后是一个进程来处理的,那处理结果必然存在这个进程自己的内存空间中,这样别的进程想读取的话怎么办?IPC?消耗多大! 正是因为多进程各自的内存空间是独立的,导致传入的数据也是相互独立的,而不是存放在一起的!这样想跨进程查询的话就得IPC,而且及其频繁,而且还不知道是该查哪个子进程的空间!所以这种方式明摆着不合适,那如果我们是各个子进程获取到用户传入的数据之后:不是保存在自己的进程中,而是还要写入到一个多进程共享的内存中,这样好使得数据变为全局来避免IPC呢?这种实现的话就相当于:我们将每个进程传入的数据都写入到进程共享内存中!而各个进程只要操作这个共享内存就可以了。这个实现当然可以,没什么问题,但是显然没有直接单进程多线程来的easy,这样将所有数据直接放在进程空间中,而非共享内存中!操作起来也速度,简单!


所以从上边的角度来将:之所以选择多线程是基于全局数据的考虑,各个处理请求的进程/线程 都要全局的读取,而不是各自空间内读取!!

那因为多个处理进程/线程是同时读写同一份大数据,必然牵扯到同步的问题,避免同时写之类的操作。所以还加一些锁。


而mysql的实现呢?也是用的多线程,按照上边的原因也很容易解释的通。各个请求到来之后:需要各个处理worker来操作全局数据,所以要求这些workers都能访问同一片数据。所以多线程是中很合适的方式,大家都处于同一个进程的内存空间中! 当然:也牵扯读写锁的问题来同步等~~


=============================================================================================================

那nginx呢?为什么用的都是多进程??

其实这里一句话就可以解释:其只是读取server脚本,不会更改。

请求到达nginx后,其如果不用cgi协议的话,就是直接启动多个进程来调用后端server脚本并运行之。 所以:nginx只负责读取server脚本,比如一个Php脚本.

其是一个读取,不牵扯写入/更新··

那什么感觉nginx也会写入数据呢?其实这里写入数据是写入到数据库中,php/python脚本仅仅是加在中间的一个处理层·,这一层仅仅是个媒介!!!没有具体的数据写入

所以一个写请求到达nginx之后:其要做的工作就是先读php脚本,而后php脚本再调用mysql来写全局数据库!那这样的话:nginx仅仅是读脚本,不牵扯任何写!!Php脚本仅仅是nginx的一个媒介而已~~借助php来操作数据! 所以真正的读写数据都是在后端的Mysql数据库这里,在这里就使用多线程来操作全局数据(memcache的全局数据存在内存,而Mysql的全局数据存在硬盘而已)~~而nginx仅仅是读取php脚本,而且各个php脚本都是相互独立的,所以读取时不需要同步之类的,所以这里用多线程和多进程都可以做到!!那为毛用多进程呢?因为:多进程的话各个进程之间相互独立,一个crash不会影响其他进程;而多线程:任何一个子线程crash了,其他子线程就全部死掉了!这意味着:使用多线程的话:各个用户的请求处理是相互影响的,一个挂了,其他人的请求也跟着悲剧;而多进程就不会相互影响! 所以更适合使用多进程!


亦即:我觉得nginx使用多进程和多线程在其他方面没什么区别,最大考虑的因素是相互影响的问题:为了使各个请求处理不相互影响,从而最终选择多进程!!

===================================================================================================================

总结一下:我认为牵扯到全局数据写入/更新的操作,就用多线程;否则就多进程提高健壮性吧。

-----------------------------------------------------------------------------------------------------------------------------------------------

进程池与线程池

自己胡乱写一点理解,以作记录,没有逻辑,没有主题,没有结论,欢迎拍拍。

     这两种模式在服务器开发中还是用得比较多。技术本无所谓好坏,不同技术都有自己的特点和缺点,只有他们的适用场合不同而已。进程池的最大好处在于任务的隔离性,不同进程处理的任务之间是相互独立,互不影响的,即使某个进程再处理某个用户的某个请求是crash了,也不会影响整个程序的执行。而线程池则正好相反,某个现场崩溃了,其他线程也不会有好果子吃,大家一起归西!很多人选择进程池就是因为这个原因,但是也不意味着进程池里面就可以随便crash了,作为服务器程序员,不断追求完美,处理所以可能和不可能发生的异常情况,精雕细琢内存的使用本来就是份内事.

    线程池的优点之一就是现场切换代价小,而且数据共享容易(这也可以说成是它的缺点),处理重复的小任务还是比较好的。

     市面上有些进程池框架是对每个用户连接一个进程进行处理,这种方式下并发量恐怕上不来(如果你的应用本来就不要球并发就没的说了),这样每个用户就和一个特定进程关联了。银行系统里面应该用得比较多,他们不缺钱,他们要尽可能杜绝用户之间相互影响。如果有一个主进程来处理解析协议和业务逻辑,每个进程只是作为工作进程,从队列里面取任务进行处理,如果进程过多,切换开销会很客观,但是如果你本来就是多核的服务器,而且进程数很少,且在这样的配置下,你的业务运行得很好,那也没的说了。


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页