Python_阻塞IO、非阻塞IO、IO多路复用
0、承上
进程:
计算机里最小的资源分配单位;
数据隔离, 利用多核,数据不安全。
线程:
计算机中最小的CPU调度单位;
数据共享,GIL锁,数据不安全.
协程:
线程的一部分,是有用户来调度的;
数据共享,数据安全.
异步: 同时做不止一件事情.
同步: 事情一件接着一件 的做.
阻塞: recv、recvfrom、accept、sleep、input
非阻塞:平时遇见的处过上边基本上都是。
IO操作:
网络相关的操作
文件处理、json.dump/load、logging、print、input、recv/send、connect/accept、recvfrom/sendto
recv为什么要阻塞?
等待数据到来到Python程序的内存中。
IO模型一共有五种,由于信号驱动IO不常用,我们这里主要介绍阻塞IO、非阻塞IO、IO多路复用以及异步IO。
对于一个network IO(这里以read举例),它会涉及到两个系统对象,一个是调用这个IO的process(or thread),另一个就是系统内核(kernel).当一个read操作发生时,该操作会经历两个阶段:
(1) 等待数据准备( Waiting for the data to be ready )
(2) 将数据从内核拷贝到进程中( Copying the data from the kernel to the process )
这些IO模型的区别就在这两个阶段上各有不同.
1、阻塞IO( Blocking IO )
在Linux中,默认情况下所有的socket都是blocking读操作流程大概如下:
1 import socket 2 3 sk = socket.socket() 4 sk.setblocking(True) 5 6 # True 阻塞 7 # False 非阻塞 8 # TCP协议的socket sever不能同时接收多个请求 9 # sk.accept() 10 # while True: 11 # conn.recv()
2、非阻塞IO(non-blocking IO)
非阻塞的形式实现了并发的socket server.
非阻塞的形式实现了并发的socket sever,太耗CPU.
没有数据来的时候程序的高速处理极大地占用了CPU资源.
1 import socket 2 3 sk = socket.socket() 4 sk.bind(('127.0.0.1', 9000)) 5 sk.setblocking(False) 6 sk.listen() 7 conn_lst = [] 8 del_lst = [] 9 while True: 10 try: 11 conn,addr = sk.accept() # 非阻塞,没有连接来就报错 12 conn_lst.append(conn) 13 print(conn) 14 except BlockingIOError: 15 for con in conn_lst: 16 try: 17 con.send(b'hello') 18 try: 19 print(con.recv(1024)) # 非阻塞 没有消息来就报错 20 except BlockingIOError: # 没有消息就报错 21 pass 22 except ConnectionResetError: # send没有连接的报错 23 con.close() 24 del_lst.append(con) 25 for con in del_lst: 26 conn_lst.remove(con) 27 del_lst.clear()
1 import socket 2 3 sk = socket.socket() 4 sk.connect(('127.0.0.1', 9000)) 5 while True: 6 print(sk.recv(1024)) 7 sk.send(b'bye') 8 sk.close()
sever:
结果:
D:\Python36\python.exe E:/Python/草稿纸0.py <socket.socket fd=268, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 51839)> b'bye' b'byebye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'bye' b'byebye' b'bye' b'bye' b'bye' b'byebye' b'bye' b'bye' b'bye' b'bye' Process finished with exit code 1
D:\Python36\python.exe E:/Python/草稿纸.py b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hello' b'hellohellohello' b'hello' b'hello' b'hellohellohello' b'hello' b'hello' b'hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello' b'hellohello' b'hello'
由于通信的一直是小数据块(大小不够1024),出现了黏包现象.
3、IO多路复用(IO multiplexing)
1 import select 2 import socket 3 4 sk = socket.socket() 5 sk.bind(('127.0.0.1', 9000)) 6 sk.setblocking(False) 7 sk.listen() 8 9 rlst = [sk] # 监听的是对象的读操作 10 wlst = [] # 监听的是对象的写操作 11 xlst = [] # 监听的是对象的异常操作 12 while True: 13 rl,wl,xl = select.select(rlst, wlst, xlst) 14 for obj in rl: 15 if obj == sk: 16 conn,addr = sk.accept() 17 rlst.append(conn) 18 else: 19 msg = obj.recv(1024) 20 if msg == b'': 21 obj.close() 22 rlst.remove(obj) 23 continue 24 print(msg) 25 obj.send(b'hello')
1 import socket 2 3 sk = socket.socket() 4 sk.connect(('127.0.0.1', 9000)) 5 while True: 6 sk.send(b'wahaha') 7 print(sk.recv(1024)) 8 sk.close()
socketsever —— TCP协议的并发操作 selectors + 多线程
IO多路复用的select的工作机制
select Windows 轮询
poll Linux 轮询,poll能够监听的对象比select要多
epoll Linux 不是采用轮询的方式,而是采用回调函数的形式