事件驱动模型

上节的问题: 
协程:遇到IO操作就切换。 
但什么时候切回去呢?怎么确定IO操作完了?

传统的编程是如下线性模式的:

开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束

每一个代码块里是完成各种各样事情的代码,但编程者知道代码块A,B,C,D...的执行顺序,唯一能够改变这个流程的是数据。输入不同的数据,根据条件语句判断,流程或许就改为A--->C--->E...--->结束。每一次程序运行顺序或许都不同,但它的控制流程是由输入数据和你编写的程序决定的。如果你知道这个程序当前的运行状态(包括输入数据和程序本身),那你就知道接下来甚至一直到结束它的运行流程。

 对于事件驱动型程序模型,它的流程大致如下:

开始--->初始化--->等待

 与上面传统编程模式不同,事件驱动程序在启动之后,就在那等待,等待什么呢?等待被事件触发。传统编程下也有“等待”的时候,比如在代码块D中,你定义了一个input(),需要用户输入数据。但这与下面的等待不同,传统编程的“等待”,比如input(),你作为程序编写者是知道或者强制用户输入某个东西的,或许是数字,或许是文件名称,如果用户输入错误,你还需要提醒他,并请他重新输入。事件驱动程序的等待则是完全不知道,也不强制用户输入或者干什么。只要某一事件发生,那程序就会做出相应的“反应”。这些事件包括:输入信息、鼠标、敲击键盘上某个键还有系统内部定时器触发。

一、事件驱动模型介绍

通常,我们写服务器处理模型的程序时,有以下几种模型:

(1)每收到一个请求,创建一个新的进程,来处理该请求; 
(2)每收到一个请求,创建一个新的线程,来处理该请求; 
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求

第三种就是协程、事件驱动的方式,一般普遍认为第(3)种方式是大多数网络服务器采用的方式 

论事件驱动模型 

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9 
10 <p onclick="fun()">点我呀</p>
11 
12 
13 <script type="text/javascript">
14     function fun() {
15           alert('约吗?')
16     }
17 </script>
18 </body>
19 
20 </html>
事件驱动之鼠标点击事件注册

在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢? 两种方式:

1创建一个线程循环检测是否有鼠标点击

      那么这个方式有以下几个缺点:

  1. CPU资源浪费,可能鼠标点击的频率非常小,但是扫描线程还是会一直循环检测,这会造成很多的CPU资源浪费;如果扫描鼠标点击的接口是阻塞的呢?
  2. 如果是堵塞的,又会出现下面这样的问题,如果我们不但要扫描鼠标点击,还要扫描键盘是否按下,由于扫描鼠标时被堵塞了,那么可能永远不会去扫描键盘;
  3. 如果一个循环需要扫描的设备非常多,这又会引来响应时间的问题; 
    所以,该方式是非常不好的。

2 就是事件驱动模型 

目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:

  1. 有一个事件(消息)队列;
  2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
  3. 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
  4. 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数; 
    IO model
    事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。 
     
    让我们用例子来比较和对比一下单线程、多线程以及事件驱动编程模型。下图展示了随着时间的推移,这三种模式下程序所做的工作。这个程序有3个任务需要完成,每个任务都在等待I/O操作时阻塞自身。阻塞在I/O操作上所花费的时间已经用灰色框标示出来了。 
    IO model

最初的问题:怎么确定IO操作完了切回去呢?通过回调函数 

1.要理解事件驱动和程序,就需要与非事件驱动的程序进行比较。实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的。早期则存在许多非事件驱动的程序,这样的程序,在需要等待某个条件触发时,会不断地检查这个条件,直到条件满足,这是很浪费cpu时间的。而事件驱动的程序,则有机会释放cpu从而进入睡眠态(注意是有机会,当然程序也可自行决定不释放cpu),当事件触发时被操作系统唤醒,这样就能更加有效地使用cpu.
2.再说什么是事件驱动的程序。一个典型的事件驱动的程序,就是一个死循环,并以一个线程的形式存在,这个死循环包括两个部分,第一个部分是按照一定的条件接收并选择一个要处理的事件,第二个部分就是事件的处理过程。程序的执行过程就是选择事件和处理事件,而当没有任何事件触发时,程序会因查询事件队列失败而进入睡眠状态,从而释放cpu。
3.事件驱动的程序,必定会直接或者间接拥有一个事件队列,用于存储未能及时处理的事件。
4.事件驱动的程序的行为,完全受外部输入的事件控制,所以,事件驱动的系统中,存在大量这种程序,并以事件作为主要的通信方式。
5.事件驱动的程序,还有一个最大的好处,就是可以按照一定的顺序处理队列中的事件,而这个顺序则是由事件的触发顺序决定的,这一特性往往被用于保证某些过程的原子化。
6.目前windows,linux,nucleus,vxworks都是事件驱动的,只有一些单片机可能是非事件驱动的。
事件驱动注解

注意,事件驱动的监听事件是由操作系统调用的cpu来完成的

 

IO多路复用

    I,input
    o,output

 1 #基于IO多路复用+socket实现并发请求(一个线程100个请求)
 2 
 3 import socket
 4 import select
 5 
 6 client1 = socket.socket()
 7 client1.setblocking(False) # 百度创建连接: 非阻塞
 8 
 9 try:
10     client1.connect(('www.baidu.com',80))
11 except BlockingIOError as e:
12     pass
13 
14 
15 client2 = socket.socket()
16 client2.setblocking(False) # 百度创建连接: 非阻塞
17 try:
18     client2.connect(('www.sogou.com',80))
19 except BlockingIOError as e:
20     pass
21 
22 
23 client3 = socket.socket()
24 client3.setblocking(False) # 百度创建连接: 非阻塞
25 try:
26     client3.connect(('www.oldboyedu.com',80))
27 except BlockingIOError as e:
28     pass
29 
30 socket_list = [client1,client2,client3]
31 conn_list = [client1,client2,client3]
32 
33 while True:
34     rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)
35     # wlist中表示已经连接成功的socket对象
36     for sk in wlist:
37         if sk == client1:
38             sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
39         elif sk==client2:
40             sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')
41         else:
42             sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')
43         conn_list.remove(sk)
44     for sk in rlist:
45         chunk_list = []
46         while True:
47             try:
48                 chunk = sk.recv(8096)
49                 if not chunk:
50                     break
51                 chunk_list.append(chunk)
52             except BlockingIOError as e:
53                 break
54         body = b''.join(chunk_list)
55         # print(body.decode('utf-8'))
56         print('------------>',body)
57         sk.close()
58         socket_list.remove(sk)
59     if not socket_list:
60         break
单线程的并发

相关文章:

  • 2021-09-08
  • 2021-12-15
  • 2022-02-25
  • 2022-12-23
  • 2021-11-21
  • 2022-01-07
  • 2021-07-30
  • 2021-04-20
猜你喜欢
  • 2022-03-06
  • 2018-08-19
  • 2022-12-23
  • 2022-01-01
  • 2021-09-25
  • 2022-01-22
相关资源
相似解决方案