多路复用IO和异步IO

多路复用I/O

它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

流程图如下:

  

 

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

强调

1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

    2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

    结论: select的优势在于可以处理多个连接,不适用于单个连接  

 

 1 from socket import *
 2 import time
 3 import select
 4 
 5 
 6 server = socket(AF_INET, SOCK_STREAM)
 7 server.bind(('127.0.0.1',8080))
 8 server.listen(5)
 9 server.setblocking(False)
10 
11 data_dic={}
12 read_list=[server,]
13 write_list=[]
14 print('start....')
15 while True:
16     # 括号里面应该有四个参数,最后一个参数timeout默认的话就是一直在这边等待,直到接收到参数。
17     # 如果设置超时延时,那么如果在3s未接受到数据,他会自动运行下面的代码。但是如果他在1s就接收到数据,
18     # 就会直接执行下面程序。
19     rl,wl,xl=select.select(read_list,write_list,[]) #read_list=[server,conn1,conn2,conn3,conn4]
20     # print('read_list:%s rl:%s wl:%s ' %(len(read_list),len(rl),len(wl))) #rl=[conn1,conn2]
21     for sk in rl:
22         if sk == server:
23             conn,addr=sk.accept()
24             read_list.append(conn)
25         else:
26             # sk.recv(1024)
27             # print(sk)
28             data=sk.recv(1024)
29             write_list.append(sk)
30             data_dic[sk]=data
31 
32     for sk in wl:
33         sk.send(data_dic[sk].upper())
34         data_dic.pop(sk)
35         write_list.remove(sk)
服务端
 1 from socket import *
 2 import os
 3 
 4 client=socket(AF_INET,SOCK_STREAM)
 5 client.connect(('127.0.0.1',8080))
 6 
 7 while True:
 8     msg='%s say hello' %os.getpid()
 9     client.send(msg.encode('utf-8'))
10     data=client.recv(1024)
11     print(data.decode('utf-8'))
客户端

 

异步IO

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。流程图如下:

  

 

 

上节课复习:
1、协程
什么是?
协程指的是单线程下由应用程序级别实现的并发
即把本来由操作系统控制的切换+保存状态,在应用
程序里实现了

协程的切换vs操作系统的切换
优点:
切换速度远快于操作系统
缺点:
一个任务阻塞了,其余的任务都无法执行

ps:只有遇到io才切换到其他任务的协程才能提升
单线程的执行效率

为何用?
把单个线程的io降到最低,最大限度地提升单个线程的执行效率

如何实现?
from gevent import spawn,monkey;monkey.patch_all()

2、io模型
block io
nonblocking io
1、对cpu的无效占用率过高
2、不能即时反馈客户端的信息

 

posted @ 2018-07-18 20:35  Roc_Atlantis  阅读(1031)  评论(0编辑  收藏  举报