网络编程大并发实战-IO并发线程模型

本篇章只讨论基于前文介绍的IO模型引申出来的狭义的IO并发编程模型,而非纯粹并发编程模型。基本上就是线程,进程相关的并发编程模型,需要了解其他诸如协程等其他并发编程模型,请边上走。

进程与线程

早期的单进程或者单线程的方式我们就不说,这里一般就是讨论的多进程及多线程的方式,涉及到多进程多线程就会产生资源的竞争,CPU的系统资源的竞争就会需要上下文切换,这是要耗费资源的。这里只对进程及线程的切换可能的影响做一个比较:

进程上下文切换内容:

  • 通用寄存器、状态寄存器、程序计数器等各种CPU寄存器
  • 内核栈、用户栈
  • page table, 用于虚拟地址空间和物理地址空间映射
  • process table,保存进程id,优先级,环境变量等
  • file table,保存当前进程打开文件的状态信息

线程上下文切换内容:

切换CPU中的各种寄存器。

一般来说一种方式的 优缺点都是相对的,并且一般都是由于某种特性引起的,那么多进程的优缺点也是由于进程如下特性引起的:

有独立的地址空间、可执行的代码段、打开的文件句柄、 堆、栈。

多进程的优点:简单。(正因为其地址空间的独立性,其不存在资源的竞争。所以简单)

多进程的缺点:效率低,并发能力被限制。进程切换开销:(1)用户空间与内核空间转化,(2)任务调度器的计算复杂度, (3)进程上下文切换。(进程调度是抢占式的,正因为其地址空间的独立性,当进行进程切换的时候,上下文内容就特别多,需要保存的进程缓存就比较多)

多线程的优点:上下文切换的开销要小很多。由于共享地址空间,同一个进程 的不同线程切换时,只需切换CPU中的各种寄存器,而不必进行昂贵的地址转换、缓存刷新操作。 寄存器的切换是非常高效的,通常,它的开销与一次内核态、用户态的切换相当。此外,线程间不需要 低效的IPC通信,可以访问共享的地址空间。由于线程独立顺序执行,具有较好的数据局部性(data locality)。

多线程的缺点:需要引入了原子操作、锁、条件变量、信号量等机制来解决线程互斥与同步的问题。

一个连接一个进程/线程

一般来说就是典型的 多进程/多线程 + blocking IO.   通过前面进程与线程的描述其实我们也知道,使用多进程及多线程是耗费资源的,特别是当连接数,并发数增加到一定量级之后,进程或者线程本身及其切换就会成为瓶颈。所以这种模型适合连接数或者并发数不多的情况,一般用用也是够了。

连接与进程可不可以不用一一对应,因为计算机的资源是有限。下面这种模式就解决了这个问题。

IO多路复用+多进程/线程(事件模型)

这是一个 1+n 的线程模型,一个主进程/线程,专门负责监听处理连接,当客户端连接上来之后就直接扔到select/epoll 上,轮询到有数据通信发生的时候,把处理放到多个工作线程中去,同时为了不让前面的悲剧发生,减少不必要的切换,工作线程数的设置与CPU数线性相关(一般设置为CPU内核数或者2倍(超线程))。

在这个模型中,已经可以实现大并发大连接,但是还是基于同步IO,即IO还是有阻塞。虽然连接数上来了,但是并发性能上还有优化空间,因为IO还有阻塞。那么就需要下面这个相近的并发模型了。

异步IO+多线程

这个模型跟上面的模型使用的区别就是实现真正的异步IO,其他都差不多。但这个需要操作系统内核的支持。WINDOWS系统有比较完善成熟的一套IOCP异步IO,而Linux  内核的异步IO支持稍晚,比较流行成熟的大并发还是已epoll 作为IO模型。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>