python并发编程(七)——IO模型

IO操作的时间

一个进程中IO的read操作发生时会经历两个阶段的时间:
1.进程去系统中获取数据时的等待数据准备时间
2.系统接收到发送来的数据,复制后从内核拷贝到进程中的时间
如果要想提高IO效率,常见的就是将第一步等待数据的时间降低

IO模型

IO模型一般分为:阻塞IO,非阻塞IO,信号驱动IO,IO多路转接,异步IO.前四个被称为同步IO

阻塞IO(blocking I/O)

阻塞IO:
1.进程去系统中获取数据时数据还未到达时(阻塞当前进程)
2.数据到达系统时,再从内核中拷贝到进程中
所有socket默认都是阻塞IO

非阻塞IO(noblocking I/O)

非阻塞IO:
1.进程去系统中获取数据时数据还未到达直接返回(不阻塞)
2.进程定时去系统中查询数据是否到达,是一个轮询的过程
3.当数据到达后,将数据从内核中拷贝到进程中
将socket中的阻塞IO替换为非阻塞IO
socket.setblocking(False)

非阻塞IO能在等待任务的期间去执行其它的任务
但是非阻塞IO的循环调用会使得CPU的占有率大幅度提高
from threading import Thread
import socket

def chat(conn):
    conn.send(b'hello')#发送数据
    msg = conn.recv(1024).decode('utf-8')#非阻塞,但是没有消息会报错BlockingIOError
    print(msg)
    #数据发送延迟的话,recv就无法获取到数据,延时的数据就会丢失
    conn.close()
    
sk = socket.socket()
sk.bind(('127.0.0.1',8099))
#把socket中所有阻塞的方法改为非阻塞,包括recv/recvfrom/accept
sk.setblocking(False)
sk.listen()
while True:
    conn,addr = sk.accept()#此处就不会陷入阻塞,但是没人连会报错BlockingIOError
    t = Thread(target = chat,args = (conn,))
    t.start()

sk.close()

信号驱动IO (signal blocking I/O)

信号驱动IO:
1.进程去系统中获取数据时数据还未到达直接返回(不阻塞)
2.进程去执行其它任务,当数据到达时,系统给进程一个数据到达的信号
3.进程调用IO读写操作去系统中获取数据

IO多路复用(I/O multiplexing)

IO多路复用:
1.有多个进程去系统获取数据时,找一个代理对象,让这个对象不断去轮询多个 socket 的状态
2.只有当socket真正有读写事件时,才真正调用实际的IO读写操作
3.系统拷贝数据给当前进程
IO多路复用属于阻塞IO,对多个程序进行阻塞监听,所以效率较阻塞IO高
单个线程而言多路复用效率低于非阻塞IO
但是代理对象不仅仅可以代理socket.accept()
也可以代理conn.recv()等多个对象
IO多路复用是操作系统提供的select机制
python调用这个机制的模块就是select模块
selectors模块的selectors.DefaultSelector()会自动选择当前适合的机制	
import select
sk = socket.socket()
sk.bind(('127.0.0.1',8099))
r_list = [sk]
r,w,x = select.select(r_list,[],[])
#select.select(要读取数据的对象的列表,要写入操作的对象的列表,感知修改的对象的列表)
#三个参数必须传,没有就为空列表
#返回三个参数列表组成的元组r_list,w_list,x_list
#server会一直阻塞直到客户端请求到来

#返回的列表中的元素实际是要接收连接的代理对象sk
#例如r_list[]中有10个对象有监听各自的连接,一个连接来了,只会返回对应这个连接的监听对象组成的列表
#不会返回所有

for i in r:
        if i is sk:#如果返回的列表是sk对象
            conn,addr = i.accept()
            r_list .append(conn)#将conn对象添加到监听列表
        else:#返回的不是sk对象就是conn对象
            ret = i.recv(1024)#conn.recv接收数据
            if ret == b'':#conn断开的话收到的数据为空
                i.close()#关闭这个连接
                r_list .remove(i)#从监听列表中移除这个conn
                continue
            print(ret)
            i.send(b'goodbye!')
IO多路复用select机制
用select存放要监听的对象
由系统调用select机制来循环这个列表,查看是否有可读时间
这一阻塞时间在系统内部完成,与python代码无关的
有连接来,发送一个反馈给server
下面就与多路复用无关
server接收到反馈就去系统获取数据
系统拷贝数据再发送给server

多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中,不断地询问 socket状态时通过用户线程去进行的,
而在多路复用IO中,轮询每个socket 状态是内核在进行的,这个效率要比用户线程要高的多

异步IO(asynchronous I/O)

异步IO模型才是最理想的IO模型
1.进程去系统中获取数据时数据还未到达时直接返回(不阻塞)处理其他任务
2.系统记录进程的异步信息和后续补充处理程序,开启独立线程执行IO操作
3.当数据到达后,系统获取数据然后写入进程指定的缓冲区,IO操作完成后通知进程,数据的read操作已经完成
在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成
用户线程中不需要再次调用IO函数进行具体的读写
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值