很多情况下我们会遇到编程模型选择的问题:多进程 or 多线程 ? 下面简要介绍下两者区别:
多进程 | 多线程 | |
资源 | 进程是资源分配的基本单位,独占用整个进程所有资源 |
进程内所有线程共享进程资源
|
通信 | 需要借助共享内存、管道、信号量、socket等方式实现 | 由于线程资源共享,很容易实现各线程间消息通信 |
编程 | 编程调试简单,可靠性高,创建销毁系统开销大 | CPU调度的基本单位,切换速度快,资源访问互斥、同步导致编程复杂度增加,同时也不方便调试 |
信号 | 每个进程独立控制信号 | 进程内所有线程共享信号处理函数,除了SIGSEGV,SIGALRM这样的信号会直接发送给调用线程,其余的信号都默认交给主线程。信号处理逻辑复杂 |
上面表格中写出了多线程、多进程的基本区别,除了上述内容我们还有一些需要关注的点:
- 由于所有线程共享进程的资源,因此所有线程能够打开的最大文件描述符数(包含socket)之和等于进程支持的最大文件描述符数;所有线程栈(默认8MB)大小的总和等于进程访问的最大地址空间,因此如果线程创建了一个超大的局部数组或者其他结构,可能会导致所有线程所占的地址空间总和超出进程地址空间的范围进而触发SIGSEGV段错误,致使整个程序崩溃。
- 多线程的优势在于共享进程中的全局资源以及堆区资源,极大方便了各个线程间信息的交换;在带来方便的同时,为了保证每个线程在访问这些共享资源时的正常访问,需要对这些共享资源的访问添加互斥、同步机制,这就增加了多线程程序的开发、调试难度,程序的稳定性难以把控。
- 信号处理在多线程程序开发时,也是需要考虑的一点;建议在多线程程序中,创建单独的线程利用sigwait等函数同步处理信号,其余线程直接屏蔽信号,这样避免了注册信号处理函数这种异步处理方式导致的问题。
之前看到很多人疑问,为什么高性能的nginx、redis要采用多进程模型而不是多线程模型?
我来根据上面对多进程、多线程的分析来尝试解答下:
- nginx、redis每个单独的进程都可以独占资源,通常情况下每个服务器会开几十个nginx、redis进程,这样如果采用多线程的模式,各个线程能够利用的资源就会受到一些限制。诸如:ulimit -n 命令展示的每个进程最多可以打开的文件数(这个可以改的;另外nginx文件读写操作不多,这里不是问题)这样的限制。
- nginx中除了master需要跟worker通过管道进行通信,worker之间不需要通信,而且每个worker的功能都一样,属于常驻进程。在这种场景下多线程的优势体现不出来,而且也可以避免多线程在编程时需要考虑资源访问互斥、同步等问题带来的编程复杂度的提升,以及可能带来的调试困难。(PS:这句话的同步、互斥并非指线程间消息传递等操作,这种操作就像评论中@sevencatwang 说的,在多进程中开销更大)
- nginx采用多进程的方式,既可以避免因某个线程故障导致整个服务不可用的问题,也可以实现配置热加载,不停服升级版本。
注:nginx是有线程池的选项的,但是该线程池也是在每个worker中用于处理业务的,对于连接的处理还是由主线程来完成的;在这里多线程只是对nginx多进程的一个补充,而不是替代。
redis目前在执行aof、bgsave等操作时,由于是单线程,所以会导致请求延时临时增大,服务不稳定。redis的作者希望在将来引入线程来处理这种IO密集型的操作