• IO模型


    首先我们来复习一下昨天的协程

    协程的目的:单线程下实现并发(切换+保存状态)

    yiekd保存状态(自己控制自己)

    yield来回切换是一种并发的,但是对效率没有用

    切换:遇到I/O时切换,cpu运行时间过长切换

    单纯的切没有意义,遇到I/O时会提高效率,才有意义,但是yield做不到

    yield是python提供的

    多进程多线程由操作系统调度(比起yield来太复杂)

    切换要把状态保存下来

    控制多个任务来回切换遇到I/O时切,所以就用到了gevent

    同步:调用一个功能在原地等着

    异步:调用时会立马返回一个结果

    阻塞:I/o阻塞,进程和线程不阻塞

    非阻塞:不用等了

    需了解的五种IO模型:

    blocking  IO

    nonblocking IO

    IO multiplexing

    signal driven IO

    asynchronous IO

    当一个read操作发生时,会经历两个阶段:

    1.等待数据

    2.将数据从内核拷贝到进程中

    阻塞IO(blocking IO):

    特点:IO执行的两个阶段(等待数据和拷贝数据)都被block了

    实际上,除非特别指定,几乎所有的IO接口(包括socket接口)都是阻塞型的

    非阻塞IO(non-blocking IO):

    在非阻塞式IO中。用户进程其实是需要不断的主动询问kernel数据准备好了没

    #服务端
    from socket import *
    import time
    s=socket(AF_INET,SOCK_STREAM)
    s.bind(('127.0.0.1',8080))
    s.listen(5)
    s.setblocking(False) #设置socket的接口为非阻塞
    conn_l=[]
    del_l=[]
    while True:
        try:
            conn,addr=s.accept()
            conn_l.append(conn)
        except BlockingIOError:
            print(conn_l)
            for conn in conn_l:
                try:
                    data=conn.recv(1024)
                    if not data:
                        del_l.append(conn)
                        continue
                    conn.send(data.upper())
                except BlockingIOError:
                    pass
                except ConnectionResetError:
                    del_l.append(conn)
    
            for conn in del_l:
                conn_l.remove(conn)
                conn.close()
            del_l=[]
    
    #客户端
    from socket import *
    c=socket(AF_INET,SOCK_STREAM)
    c.connect(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ')
        if not msg:continue
        c.send(msg.encode('utf-8'))
        data=c.recv(1024)
        print(data.decode('utf-8'))
    View Code

    但是不推荐

    我们不能否则其优点:能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在“”同时“”执行)。

    但是也难掩其缺点:

    #1. 循环调用recv()将大幅度推高CPU占用率;这也是我们在代码中留一句time.sleep(2)的原因,否则在低配主机下极容易出现卡机情况
    #2. 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。

    多路复用IO(IO multiplexing):

     强调:

        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的优势在于可以处理多个连接,不适用于单个连接 

    #服务端
    from socket import *
    import select
    
    s=socket(AF_INET,SOCK_STREAM)
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('127.0.0.1',8081))
    s.listen(5)
    s.setblocking(False) #设置socket的接口为非阻塞
    read_l=[s,]
    while True:
        r_l,w_l,x_l=select.select(read_l,[],[])
        print(r_l)
        for ready_obj in r_l:
            if ready_obj == s:
                conn,addr=ready_obj.accept() #此时的ready_obj等于s
                read_l.append(conn)
            else:
                try:
                    data=ready_obj.recv(1024) #此时的ready_obj等于conn
                    if not data:
                        ready_obj.close()
                        read_l.remove(ready_obj)
                        continue
                    ready_obj.send(data.upper())
                except ConnectionResetError:
                    ready_obj.close()
                    read_l.remove(ready_obj)
    
    #客户端
    from socket import *
    c=socket(AF_INET,SOCK_STREAM)
    c.connect(('127.0.0.1',8081))
    
    while True:
        msg=input('>>: ')
        if not msg:continue
        c.send(msg.encode('utf-8'))
        data=c.recv(1024)
        print(data.decode('utf-8'))
    复制代码
    select网络IO模型

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

    blocking和non-blocking的区别:

    调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。

    non-blocking IO和asynchronous IO的区别:

    在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

  • 相关阅读:
    【java编程】java的关键字修饰符
    【分布式锁】redis实现
    【java高级编程】JDK和CGLIB动态代理区别
    【druid 】数据库连接池
    【druid 】数据库连接池-sql解析
    【mysql】Mha实现高可用数据库架构
    【mysql】工具使用
    7.7 服务远程暴露
    7.6 服务远程暴露
    7.5 zookeeper客户端curator的基本使用 + zkui
  • 原文地址:https://www.cnblogs.com/1996-11-01-614lb/p/7464957.html
Copyright © 2020-2023  润新知