文章目录
  1. 半同步/半异步模式
    1. 半同步/半反应堆
    2. 高效的半同步/半异步模式
  2. 领导者/追随者模式
    1. 句柄集
    2. 线程集
    3. 事件处理器与具体的事件处理器

并发模式是指I/O处理单元与多个逻辑单元之间协调完成任务的方法。

半同步/半异步模式

在并发模式中,”同步”指程序完全按照代码序列的顺序执行,”异步”指程序的执行需要由系统事件来驱动。常见的系统事件包括中断、信号等。

在半同步/半异步模式中,同步线程用于处理客户逻辑,异步线程用于处理I/O事件。异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中;请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象。

半同步/半异步工作流程图如下。

 

综合考虑两种事件模式与多种I/O模型,半同步/半异步模式就存在多种变体。

半同步/半反应堆

半同步/半反应堆模式图如下,采用Reactor模式。

 

只有一个异步线程,即主线程;主线程负责监听所有socket上的事件,若监听socket上有可读事件发生(即新的连接请求到来),主线程将其接受从而得到新的连接socket,再向epoll内核事件表中注册该socket上的读写事件。

若连接socket上有读写事件发生(即有新的客户请求到来或有数据发送到客户端),主线程就将该连接socket插入请求队列;所有工作线程都睡眠在请求队列上,当有任务到来时,它们通过竞争获得任务的接管权;这种竞争机制使得只有空闲的工作线程才有机会来处理新任务。

半同步/半反应堆具有如下缺点:

  • 由于主线程与工作线程共享请求队列,因此在主线程向请求队列中添加任务或工作线程从请求队列中取出任务时都需要对请求队列进行加锁保护,从而浪费CPU时间;
  • 每个工作线程在同一时间只能处理一个客户请求。在客户数量很多时将使得服务器的响应速度变慢;而增加工作线程导致的线程切换又会耗费大量的CPU时间;

高效的半同步/半异步模式

为避免半同步/半反应堆的缺点,如下的高效半同步/半异步模式让每个工作线程都能同时处理多个客户连接。

 

其中,主线程只管监听socket,而连接socket的工作由工作线程来完成。当有新的连接到来时,主线程就将其接受并将新返回的连接socket分配给某个工作线程,此后该socket上的所有I/O操作均由工作线程来完成,直到客户端关闭连接。

主线程向工作线程分配socket最简单的方式为主线程向它与工作线程之间的管道中写数据;工作线程检测到管道中有数据可读时,就分析是否为一个新的客户连接请求,如果是就将此新的socket上的读写事件注册到自己的epoll内核事件表中。

因此,这种高效的半同步/半异步模式中,所有的线程都工作在异步模式。

领导者/追随者模式

领导者/追随者模式是多个工作线程轮流获得事件源集合,轮流监听、分配并处理事件的一种模式。

在任意时刻,服务器都只有一个领导者线程,它负责监听I/O事件;其他线程均为追随者,它们忙于处理I/O事件或休眠在线程池中等待成为新的领导者。

当前领导者检测到I/O事件后从线程池中推选出新的领导者线程,然后它去处理I/O事件;以此来实现并发。

要实现领导者/追随者模式需要如下组件:句柄集(HandleSet),线程集(ThreadSet),事件处理器(EventHandle)以及具体的事件处理器(ConcreteEventHandle)。

句柄集

句柄(Handle)表示I/O资源,句柄集管理众多句柄,它使用wait_for_event方法来监听这些句柄上的I/O事件,并将其中的就绪事件通知给领导者线程。领导者调用绑定到Handle上的事件处理器来处理事件。Handle与事件处理器的绑定是通过调用句柄集中的register_handle方法实现的。

线程集

线程集是所有工作线程的管理者。它负责各线程之间的同步,以及新领导者线程的推选。线程集中的线程必处于如下三种状态之一:

  • Leader:领导者线程,负责等待句柄集上的I/O事件;
  • Processing:线程正在处理事件;领导者检测到I/O事件后,可以转移到Processing状态来处理该事件,并调用promote_new_leader方法推选新的领导者,也可以指定其他追随者来处理事件,此时领导者地位不变;处于processing状态的线程处理完事件之后,若当前线程集中没有领导者,该线程将成为领导者,否则成为追随者;
  • Follower:线程当前处于追随者身份,通过调用线程集的join方法等待成为新的领导者,也可能被当前的领导者指定来处理新的任务;

注意到领导者线程推选出新的领导者和追随者等待成为新领导者这两个操作都将修改线程集,因此线程集提供一个成员Synchronizer来同步这两个操作,以避免竞态条件。

事件处理器与具体的事件处理器

事件处理器通常包含一个或多个回调函数handle_event,这些回调函数用于处理事件对应的业务逻辑。事件处理器在使用前需要被绑定到某个句柄上,当该句柄上有事件发生时,领导者就执行与之绑定的事件处理器中的回调函数。具体的事件处理器是事件处理器的派生类,它需要重新实现基类的handle_event方法,以处理特定的任务。

 

领导者线程自己监听I/O事件并处理客户请求,因此领导者/追随者模式不需要在线程之间传递任何额外的数据,也无需像半同步/半反应堆模式那样在线程之间同步对请求队列的访问。

但是领导者/追随者模式只支持一个事件源集合,无法让每个工作线程独立地管理多个客户连接。