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:Python36python.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:Python36python.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')
IO多路复用-sever端
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()
IO多路复用-client端

  socketsever —— TCP协议的并发操作  selectors + 多线程

  IO多路复用的select的工作机制

    select  Windows  轮询

    poll      Linux        轮询,poll能够监听的对象比select要多

    epoll    Linux        不是采用轮询的方式,而是采用回调函数的形式

原文地址:https://www.cnblogs.com/ZN-225/p/9203674.html