在探讨并发模式前,我们需要知晓什么是并发编程。
并发编程的目的:让程序“同时”执行多个任务,提高效率。
当一个程序是计算密集型时,并发编程并没有优势,反而由于任务的切换使效率降低。
但是,当一个程序是I/O密集型时,采用并发编程会极大地提高CPU的利用率。
因为I/O操作的速度远远小于CPU的计算速度,所以让程序阻塞于I/O操作上会浪费大量的CPU时间。而并发编程可以让阻塞于I/O操作的线程主动放弃CPU,将执行权转移到其他线程。 这样的话,CPU就可以用来做更加有意义的事情啦,而不是一直傻傻的等待I/O操作完成。
言归正传啦,这下开始说说并发模式。
并发模式
即IO处理单元和多个逻辑单元之间协调完成任务的方法。
在服务器上,主要有两种并发模式:半同步/半异步模式(half-sync/half-async)和领导者-追随者模式(Leader/Followers)
一、半同步/半异步模式
1. 半同步半异步模式
同步线程用于处理客户逻辑(逻辑单元);异步线程用于处理I/O事件(I/O处理单元)。异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中。请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象。
是不是觉得“同步”“异步”这两个词听起来有点熟悉,但是,这与前面讨论的I/O模型中的“同步”和“异步”是完全不同的概念。
在并发模式中,“同步”指的是程序完全按照代码序列的顺序执行;“异步”指的是程序的执行需要由系统事件来驱动。
常见的系统事件包括中断、事件等。
并发模式中的同步与异步如图1所示:

按照同步方式运行的线程称为同步线程,按照异步方式运行的线程称为异步线程。
显然,异步线程的执行效率高,实时性强,这是很多嵌入式程序采用的模型。但编写以异步方式执行的程序相对复杂,难于调试和扩展,而且不适合于大量的并发。而同步线程则相反,它虽然效率相对较低,实时性较差,但逻辑简单。
因此,对于像服务器这种既要求较好的实时性,又要求能同时处理多个客户请求的应用程序,我们就应该同时使用同步线程和异步线程来实现,即采用半同步/半异步模式来实现。
2. 半同步半反应堆模式
在服务器程序中,如果结合考虑两种事件处理模式和几种I/O模型,则半同步/半异步模式就存在多种变体。其中有一种变体称为半同步/半反应堆模式(half-sync/half-reactive)。
如图2所示:

在图2中,异步线程只有一个,由主线程来充当。它负责监听所有 socket 上的事件。
(1)如果监听 socket 上有可读事件发生,即有新的连接请求到来,主线程就接受之以得到新的连接 socket,然后往 epoll 内核事件表中注册该 socket 上的读写事件。
(1)如果连接 socket 上有读写事件发生,即有新的客户请求到来或有数据要发送至客户端,主线程就将该连接 socket 插入请求队列中。
所有工作线程都睡眠在请求队列上,当有任务到来时,它们将通过竞争(比如申请互斥锁)获得任务的接管权。这种竞争机制使得只有空闲的工作线程才有机会来处理新任务,这是很合理的。
在图2中,主线程插入请求对立的任务是就绪的连接 socket。这说明了半同步/半反应堆模式采用的事件处理模式是Reactor模式:它要求工作线程自己从 socket 上读取客户请求和往 socket 写入服务器应答。这就是该模式的名称中"half-reactive”的含义。
半同步/半反应堆模式存在如下缺点:
(1)主线程和工作线程共享请求队列。主线程往请求队列中添加任务,或者工作线程从请求队列中取出任务,都需要对请求队列加锁保护,从而白白耗费CPU时间。
(2)每个工作线程在同一时间只能处理一个客户请求。 如果客户数量较多,而工作线程较少,则请求队列中将堆积很多任务对象,客户端的响应速度将越来越慢。如果通过增加工作线程来解决这一问题,则工作线程的切换也将耗费大量CPU时间。
3. 高效的半同步/半异步模式
在这种模式中,每个工作线程都能同时处理多个客户连接。
如图3所示:

二、领导者/追随者模式
领导者/追随者模式是多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。
在任意时间点,程序都仅有一个领导者线程,它负责监听I/O事件。而其他线程则都是追随者,它们休眠在线程池中等待成为新的领导者。当前的领导者如果检测到I/O事件,首先要从线程池中推选出新的领导者线程,然后处理I/O事件。此时,新的领导者等待新的I/O事件,而原来的领导者则处理I/O事件,二者实现了并发。
领导者/追随者模式包含如下几个组件:句柄集(HandleSet)、 线程集(ThreadSet)、事件处理器( EventHandler)和具体的事件处理器(ConcreteEventHandler).。它们的关系如图4所示。

领导者/追随者模式的优缺点:
优点:自己监听I/O事件并处理客户请求,也就是说从接收到处理都是在同一线程中完成,所以不需要在线程之间传递任何额外的数据,也不用像半同步/半反应堆模式那样在线程间同步对请求队列的访问;
缺点:仅仅只支持一种事件源集合,因此导致它不能让每个工作线程独立地管理多个客户连接。
传送门: