48. 半同步/半异步


本节说到的半同步与半异步跟同步和异步并不是一个概念, 请不要混淆了. 也为了避免混淆, 下面也会对同步和异步做一个简要的说明.


同步/异步

同步和异步是属于IO模型中, 前面已经对IO模型有过接触了( 如 非阻塞IO[1]).

  • 同步 : 内核通知进程有数据到来, 进程从内核空间中将数据复制到应用空间中.
  • 异步 : 内核将数据复制到指定的空间中后通知进程, 进程只管处理数据. 异步不会阻塞.

以上同步和异步简单描述起来可能还是有人不太明了两者的区别. 一句话 : 同步需要进程自己复制数据, 而异步是内核复制好数据之后通知进程(不需要复制).

我们前面所有的IO模型都是同步的.


半同步/半异步

同时使用同步线程和异步线程来实现一个程序, 即半同步/半异步模式.

实现半同步/半异步有三个模块 : 异步处理模块(异步线程), 同步处理模块(同步线程)和队列模块

  • 同步线程 : 处理客户逻辑 (工作线程处理事件).
  • 异步线程 : 处理IO事件.
  • 请求队列 : 保存正在等待被处理的就绪事件队列.

同步线程与异步线程之间的关联 : 异步线程监听到客户请求后, 就将其封装成请求对象插入到就绪队列中. 而请求队列将会通知某个工作在同步模式的工作线程来处理请求. 通知方式取决于实现.

关于半同步/半异步模式也有很多实现方式, 如 : 半同步/半反应堆模式.


半同步/半反应堆模式(Reactor模式)

48. 半同步/半异步

半同步/半反应堆模式中:

  1. 异步线程即主线程只有一个, 主要负责监听所有的 socket 上的事件.
  2. 主线程有就绪事件(就是上图中就绪的文件描述符), 将其加入对应事件的就绪队列中.
  3. 就绪队列中有事件时, 就唤醒所有的睡眠中的工作线程(类似于惊群), 所有工作线程竞争事件, 谁得到事件谁就处理其他的继续睡眠.

是不是觉得上面描述的过程很像前面 线程-IO复用 中的程序啊? 是的, 基本差不多, 只是少了线程池.


其实上图(图一)中的主线程仅仅只是负责监听事件, 而后所有的事都交给工作线程, 导致线程的工作量很大, 可以适当的将任务交给主线程来做, 比如主线程还负责处理读事件, 将数据复制到一个指定的内存, 处理完之后再唤醒工作线程, 此时工作线程只需要负责发送即可. 这就很像用同步模仿了异步.


上图中的缺点也很明显 :

  • 主线程和工作线程都共享请求队列, 同步处理复杂.
  • 每个工作线程同一时间只能处理一个请求




48. 半同步/半异步

上图(图二)是对前面的模式做了修改的, 一种更加高效的工作方式.

工作方式 :

  1. 主线程监听事件.
  2. 有事件就绪主线程选择一个工作线程来处理事件. (选择 : 通过主线程与工作线程间建立的管道来通知)
  3. 每个工作线程可以有自己的工作队列, 每工作线程只会处理自己的事件(工作线程之间相互独立).

通过主线程选择的方式解决了共享的问题, 每个线程有自己的队列也解决了线程只能处理一个请求的问题. 但也出现了新的小问题 :

  • 主线程需要与每个工作线程之间建立管道.
  • 需要对每个工作线程的做好负载.

小结

本节所提及的知识都是相对缩减的, 仅仅只是为之后实例做铺垫而已, 希望有兴趣的朋友可以好好查一查资料学习.

  • 同步/异步 以及 区别
  • 半同步/半异步

Reactor模式

同步,异步,阻塞和非阻塞 区别