事件驱动模型
作者:互联网
事件驱动和异步I/O
通常,服务器处理模型有以下几种:
- 接收到请求,创建新的进程处理
- 接收到请求,创建新的线程处理
- 接收到请求后,放入一个事件列表,让主进程通过非阻塞I/O方式处理请求
第1种方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第2种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第3种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式
事件驱动和单线程/多线程编程对比
下面比较下单线程、多线程及事件驱动编程模型。下图展示了随着时间推移,这三种模式下程序所做的工作。这个程序有3个任务需要完成,每个任务在等待I/O操作时阻塞自身。灰色框表示等待I/O操作的阻塞时间。
单线程同步模型中,任务依次执行,如果某个任务因为I/O阻塞了,其他任务也只能等待,直到前面的任务执行完成后才能执行。这样3个没有依赖关系的任务,需要互相等待顺序执行,大大降低了执行效率。
多线程模型中,任务分别在独立的线程中执行,不需要互相等待。在多处理器系统上可以并行处理,在单处理器系统上可以交替执行。这种方式更有效率,但是开发者需要保护共享资源,防止其被多个线程同时访问。多线程程序更加难以推断,因为这类程序不得不通过线程同步机制如锁、可重入函数、线程局部存储或者其他机制来处理线程安全问题,如果实现不当就会导致出现微妙且令人痛不欲生的bug。
事件驱动模型中,3个任务交替执行,但仍然在一个单独控制的线程中。当处理一个I/O或其他昂贵的操作时,注册一个回调到事件队列中,当I/O操作完成后继续执行。回调描述了如何处理某个事件。事件队列轮询事件,事件发生后将事件分派给事件处理器进行处理。这种方式让程序尽可能执行而不需要额外的线程。使用事件驱动模型开发者不需要担心线程安全的问题。
当我们面对如下的环境时,事件驱动模型通常是一个好的选择:程序中有许多任务,而且任务之间高度独立(因此它们不需要互相通信,或者等待彼此),而且在等待事件到来时,某些任务会阻塞。当应用程序需要在任务间共享可变的数据时,这也是一个不错的选择,因为这里不需要采用同步处理。
网络应用程序通常都有上述这些特点,这使得它们能够很好的契合事件驱动编程模型。
事件驱动模型
事件驱动模型可以用下图表示(来源于《Software Architecture Patterns》):
主要包括4个基本组件:
- 事件队列(event queue):接收事件的入口,存放待处理的事件
- 分发器(event mediator):将不同的事件发到不同的业务逻辑单元
- 事件通道(event channel):分发器与处理器的连接通道
- 事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作
比如说在Java的socket NIO模型中,socketChannel总是将总是将自身注册为对可读,可写事件感兴趣。serverSocketChannel却往往将自身注册为对新的tcp连接请求事件感兴趣。不同类型的对象可以以不同的兴趣对象注册到同一个分发器中,分发器既需要能够辨别发生了什么事件,又需要将不同的事件分派给不同的事件通道。因为分发器具有为不同兴趣的不同对象服务的能力,所以分发器需要占用一个线程。
另一个问题是事件被分派到事件通道后,事件处理器是如何知道事件发生了。事件处理器就像人一样,需要时不时地看报纸,刷手机,来了解新闻。这个动作是人主动发出的,这个是关键的。所以处理器需要一个独享的线程,在这个线程中检测事件是否发生。事件处理器线程在没有新事件传来时是阻塞的,一旦eventChannel传来了新的事件,事件处理就不再阻塞。
有一个单线程不阻塞地轮询事件队列,一旦事件发生了,就通过分发器将包装好的事件分派到事件通道,每个事件处理器单独占据一个线程,阻塞等待事件通道的事件。
事件队列是非阻塞的,事件处理器是阻塞的。另外,事件队列,包括分发器,事件通道通常是由类库实现,事件处理器的逻辑需要业务开发者实现。
事件驱动和消息驱动
事件驱动和消息驱动很类似,都是先有一个事件,然后产生一个对应的消息,将消息放入消息队列,然后由需要的项目获取。它们的不同主要在于消息是谁产生的。
事件驱动:鼠标点击,线程轮询检测点击事件发生,之后主动向系统发送消息“我点击了”,消息是主动产生的,再发送到消息队列中。
消息驱动:鼠标自己点击不需要和系统有交互,由系统(第三方)轮询检测事件是否发生,之后捕获消息放入消息队列。消息对于点击事件来说是被动产生的。
事件驱动往往和轮询机制有关,它们通常被统称为event loop。重点在于并不会给每个事件设置一个轮询线程,而是设置一个中央轮询中心,用这个轮询中心去轮询每个注册对象。轮询中心一旦检测到注册到其中的对象发生事件,就会通知对该事件感兴趣的对象。此时对该事件感兴趣的对象会调用方法处理事件。
参考文章:事件驱动和消息驱动_li_xunhuan的博客-CSDN博客_事件驱动和消息驱动
标签:模型,阻塞,事件驱动,线程,事件,轮询,处理器 来源: https://blog.csdn.net/weixin_42125148/article/details/122060041