• 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        不是采用轮询的方式,而是采用回调函数的形式

  • 相关阅读:
    Sql与Asp.Net数据类型对应
    EditPlus 使用技巧集萃
    VB.NET and C# Comparison
    测试后行之CodeSmith模板
    ASP.NET需要改进的地方
    LeetCode: Minimum Path Sum
    LeetCode: Merge k Sorted Lists
    LeetCode: Merge Intervals
    LeetCode: Maximum Subarray
    LeetCode: Median of Two Sorted Arrays
  • 原文地址:https://www.cnblogs.com/ZN-225/p/9203674.html
Copyright © 2020-2023  润新知