多线程,事件驱动与推荐引擎框架选型

  

? ?事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。多线程是另一种常用编程范式,并且更容易理解。

  

? ?高性能通用型c++网络框架星云是基于事件驱动的多进程网络框架(适用于即时通讯,数据采集,实时计算,消息推送等应用场景),已有即时通讯,埋点数据采集及实时分析的生产应用案例。经常有人问星云的每个进程里是单线程还是多线程的?又问为什么不用多线程?不用多线程又怎么处理并发问题?

  

? ?最近星云将会用于一个新的生产项目——推荐引擎,在此之前团队已有使用某知名度较高的RPC框架多线程版推荐引擎(业界许多推荐引擎都用了目前比较知名的开源RPC框架来开发)。本文不做星云与各知名RPC框架的比较,也无意说明哪个框架更适合做推荐引擎,只说明星云可以用于推荐引擎,且有信心效果会很好。最终结果如何,等推荐引擎研发出来,拭目以待。

  

? ?为什么是事件驱动而不是多线程?事件驱动无须多线程。我们先来回顾一下服务器编程范式。

  

1。服务器程序设计范式

  

? ?《UNIX网络编程》卷一里介绍了9种服务器设计范式:

  

多线程,事件驱动与推荐引擎框架选型

  

? ?九种服务器设计范式并不是全都有实用价值,在《UNIX网络编程》卷一最后一节里给出了几种TCP服务器设计范式代码示例:

  
      <李> TCP并发服务器程序,每个客户一个子进程李   <李> TCP预先派生子进程服务器程序李   <李> TCP预先派生子进程服务器程序,传递描述符   <李> TCP并发服务器程序,每个客户一个线程李   <李> TCP预先创建线程服务器程序,每个线程各自接受李   <李> TCP预先创建线程服务器程序,主线程统一接受李   
  

? ? Nginx采用的是九种服务器设计范式里的第5种“预先派生子进程,使用互斥锁上锁方式保护接受”,星云采用的是九种服务器设计范式里的第6种“预先派生子进程,由父进程向子进程传递套接字文件描述符”。

  

2。单线程,多线程以及事件驱动编程模型比较

  

? ?一个典型的事件驱动的程序,就是一个死循环,并以一个线程的形式存在,这个死循环包括两个部分,第一个部分是按照一定的条件接收并选择一个要处理的事件,第二个部分就是事件的处理过程。程序的执行过程就是选择事件和处理事件,而当没有任何事件触发时,程序会因查询事件队列失败而进入睡眠状态,从而释放cpu。

  

? ?某种意义上说,服务端程序大多是事件驱动的,或者说是IO请求事件驱动的。这里比较的编程模型里的事件驱动是指事件处理部分是异步的,即不仅IO请求事件驱动,还有IO响应事件驱动,它的特点是当外部IO响应事件发生时使用回调机制来触发相应的处理。

  

多线程,事件驱动与推荐引擎框架选型

  

? ?在单线程同步模型中,任务按照顺序执行。如果某个任务因为I/O而阻塞,其他所有的任务都必须等待,直到它完成之后它们才能依次执行。这种明确的执行顺序和串行化处理的行为是很容易推断得出的。如果任务之间并没有互相依赖的关系,但仍然需要互相等待的话这就使得程序不必要的降低了运行速度。

  

? ?在多线程模型,每个任务分别在独立的线程中执行。这些线程由操作系统来管理,在多处理器系统上可以并行处理,或者在单处理器系统上交错执行。这使得当某个线程阻塞在某个资源的同时其他线程得以继续执行。与完成类似功能的同步程序相比,这种方式更有效率,但程序员必须写代码来保护共享资源,防止其被多个线程同时访问。多线程程序更加难以推断,因为这类程序不得不通过线程同步机制如锁,可重入函数,线程局部存储或者其他机制来处理线程安全问题,如果实现不当就会导致出现微妙且令人痛不欲生的错误。另一个问题,操作系统内核在切换线程的同时也要切换线程的上下文,当线程数量过多时,时间将会被耗用在上下文切换中,所以在大并发量时,多线程结构还是无法做到强大的伸缩性。

  

? ?在事件驱动版本的程序中,3个任务交错执行,但仍然在一个单独的线程控制中。当处理I/O或者其他昂贵的操作时,注册一个回调到事件循环中,然后当I/O操作完成时继续执行。回调描述了该如何处理某个事件。事件循环轮询所有的事件,当事件到来时将它们分配给等待处理事件的回调函数。这种方式让程序尽可能的得以执行而不需要用到额外的线程。当无IO操作时每个任务占用cpu的时间又比较少,进程就会处于空闲状态。同等并发量情况下,事件驱动占用的系统资源会更好,负载足够大时,事件驱动程序可以将cpu利用到100%。事件驱动型程序比多线程程序更容易推断出行为,因为程序员不需要关心线程安全问题。

多线程,事件驱动与推荐引擎框架选型