多线程服务器的适用场合与常用编程模型

本文摘自陈硕老师的linux多线程服务端编程

1. 单线程服务器的常用编程模型

non-blocking + IO multiplexing 模型,即Reactor 模型。

Reactor模型是事件驱动模型,有一个或者多个并发输入源,类似于生产者与消费者模式,有一个或者多个生产者将事件放入一个Queue中,而一个或者多个消费者主动从队列中Poll事件来处理。
Reactor基于事件回调函数必须是非阻塞的,否则会影响其他连接的正常运行。

举例来说:
如果事件发生,socket可读,但是协议栈检查到这个新分节检验和错误,然后丢弃这个分节,这时候调用read则无数据可读,如果是阻塞,则会阻塞在read。

2. 多线程服务器常用的编程模型

one loop per thread + thread pool 这样的模式需要一个优质的基于Reactor的库来支持。

  • one loop per thread
    程序里的每一个IO线程都有一个Event Loop(non-blocking + IO multiplexing)或者叫 Reactor

  • 线程池
    是一种多线程处理形式,处理过程将任务添加到队列,然后创建线程启动任务。然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。

3. 多线程服务器适用的场合

选择 运行多个单线程的进程 这种模式

  • 必须用单线程的场合
    程序中可能会用到fork,只有单线程才可以fork

优点:能避免过分抢夺系统的计算资源
缺点:无法设置优先级

  • 适用多线程的场景
  • 共享数据可以修改
  • 提供非均质的服务(优先级差异)

4. 线程分类

一个多线程服务程序中的线程大致可以分为3类:

  • IO线程
    这类线程的主循环是IO multiplexing,阻塞地等待select/poll/epoll_wait系统调用上
  • 计算线程
  • 第三方库所用的线程

5. 例释

  • Linux能同时启动多少个线程?
    与进程空间与线程默认栈大小有关。
  • 多线程能提高并发量吗?
    多线程不能提高并发连接数;
    (1) 如果采用“一个线程一个连接”,则并发连接数与上面的同时启动线程数相关,很有限。
    (2) 如果采用“one loop per thread” ,单个event loop 处理一万个长连接并不罕见。(主线程只要监听连接的到来,之后将连接的建立分配给回调函数来处理)
  • 多线程能提高吞吐量吗?
    对于计算密集型服务,多线程不能提高吞吐量。
  • 多线程程序如何让IO和计算相互重叠,降低延迟?
    基本思路是将IO操作(通常是读写从左)通过 BlockingQueue 交给别的线程去处理。

总结

  • 服务端编程,如果工作集(服务程序响应一次请求所访问的内存大小)较大,那么就用一个多线程的进程,避免CPU cache换入换出,影响性能;否则就用单线程多进程,享受单线程编程的便利。
  • 线程不能减少工作量也不能减少CPU时间。
  • 多线程服务端编程建议使用Reactor + Thread pool 模型,采取one thread per thread + Non-blocking模式。
  • 将IO 线程与计算线程分开,需要“计算”的时间被放入BlockingQueue中,并用线程池来处理“计算”。

猜你喜欢

转载自blog.csdn.net/YoungSusie/article/details/91960074