关于c++:多线程服务器编程3多线程服务器的使用场合和常用模型

37次阅读

共计 1885 个字符,预计需要花费 5 分钟才能阅读完成。

本章钻研对象:分布式计算的网络应用程序,基本功能能够被简略演绎为“收到数据,算一算,收回去”

单线程服务器

最罕用的为“non-blocking IO + IO multiplexing”, 即 Reactor 模式,例如

  • lighttpd
  • Nginx
  • libevent
  • Java NIO
  • Twisted(Python)

此外还有 ASIO 应用的 Proactor 模式

Reactor

构造

  • 事件循环(Event-loop)
  • 应用 epoll 进入阻塞监听事件,依照事件类型调用 hanlder

特点

  • 须要非阻塞编程,回调函数必须是非阻塞的,只能在 epoll 处阻塞

    • 这导致容易割裂业务逻辑,不容易了解与保护
  • 实用于 IO 密集型

    • 计算太多也会阻塞
  • 尽管单线程模型不须要思考同步问题,但不能很好的利用多核,而如果同时应用多个过程,则势必会波及到同步问题

多线程服务器

  • 阻塞 + 每个申请新建一个线程
  • 阻塞 + 线程池
  • 非阻塞 + IO multiplexing 也即作者所称的“non-blocking IO + one loop per thread”
  • Leader/Follower 等高级模式

One loop per thread

益处

  • 线程数目固定
  • 不便地在线程间调配负载
  • IO 事件产生的线程是固定的,一个连贯会被注册到某个线程的 loop 中,并始终由这个线程负责,同一个 TCP 连贯不必思考并发

但多线程的 loop 相比单线程的 loop 的要求变高,即须要做到线程平安,重要的问题是“如何容许一个线程往另一个线程的 loop 里塞货色?”

线程池

  • 实用计算工作较多的状况,重要的基础设施是 BlockingQueue 实现的工作队列或生产者消费者队列
  • 把计算工作或待计算数据调配给线程池中的线程

过程间通信只用 TCP

其余 IPC 办法: pipe,FIFO,音讯队列,共享内存,信号
TCP 的益处:跨主机,伸缩性,双向,无负作用,可记录,可重现,容易定位故障

多线程服务器的实用场合

解决并发连贯的两种形式

  • Go goroutine,python gevent 等的语言提供的 用户态线程,由语言的 runtime 自行调度

    • 便宜,能够大量创立
    • 通常配合“看起来是阻塞的 IO”,这里指对于用户态的调度器阻塞,而不是对于操作系统阻塞,否则用户态多线程无奈成立
  • 内核级线程 配合 Reactor 模式,例如 libevent,muduo,Netty

    • 数量与 CPU 数目相当
    • 非阻塞 IO

因为 C ++ 并没有第一类的工具,所以本书只思考第二类

model 剖析

  • 单过程 + 单线程:没有伸缩性
  • 单过程 + 多线程
  • 多过程 + 单线程

    • 复制多份单线程的过程,应用多个 TCP port 提供服务
    • 主过程 +worker 过程
  • 多过程 + 多线程:汇集了 2 和 3 的毛病

单线程

  • 须要 fork 的时候
  • 须要限度程序 CPU 占用率的时候
  • 单线程的 Reactor 的次要毛病是响应工夫慢,多线程改善这一点

多线程

  • IO 吞吐(网络 / 磁盘)是瓶颈时,多线程不会减少吞吐,这时候单线程 + 单过程就够了
  • CPU 算力是瓶颈时,多线程不会减少吞吐,这时候单线程 + 多过程更适合也更简略
  • 比照多过程
    8 外围,压缩 100 个文件
    多过程:每个过程压缩 1 个文件
    多线程:每个文件用 8 个线程并行压缩
    总耗时雷同,因为 CPU 都是满载的,然而后者能更快拿到第一个压缩完的文件,这体现了多线程的低提早
    但实际上,线程间算法的并行度很难达到 100%,所以:同一个工作,多线程 或者 会比多过程吞吐量降落一点,但 肯定 能够带来响应工夫的晋升。

多线程的益处在于

  • 进步响应速度,让 IO 和计算重叠
  • 相比过程每次切换都使 CPU Cache 生效,线程间切换老本小,因而实用于“工作集”比拟大的状况
  • 宰割事务,将 IO 线程、计算线程和第三方库(例如 logging)线程离开

多线程应用 BlockingQueue 在过程间传递数据

  • 计算操作:一个 IO 线程,8 个计算线程(线程池),IO 线程向 BlockingQueue 中增加数据,计算线程收到唤醒后开始计算
  • 写操作:一个独自的 logging 线程,通过一个或多个 BlockingQueue 对外提供接口,别的线程把日志写入 Queue 中即可,不须要期待,这样升高了服务线程的响应工夫

    • 留神,读操作并不能利用多线程的便利性,因为无论如何都须要等到后果之后能力持续进行

线程池

  • 当计算在每次响应申请中占比比拟高时(20% 以上)适宜
  • 能简化编程;防止实时创立线程的开销;加重 IO 线程的累赘(IO 线程进行计算的话,就不能相应 IO 了,会导致响应速度变差)
  • 线程池的大小须要阻抗匹配,例如 8 核 CPU,每个计算工作线程的密集计算所占工夫比重为 50%,那么 16 个线程就能跑满全副 CPU,线程池太小,会不能高效利用硬件,太大会导致额定的线程切换和内存开销

多线程不能缩小工作量,而是一种兼顾的思路让工作提前完结。

正文完
 0