IO并发
IO 分类
IO分类:阻塞IO ,非阻塞IO,IO多路复用,异步IO等
阻塞IO
1.定义:在执行IO操作时如果执行条件不满足则阻塞。阻塞IO是IO的默认形态。
2.效率:阻塞IO是效率很低的一种IO。但是由于逻辑简单所以是默认IO行为。
3.阻塞情况:
- 因为某种执行条件没有满足造成的函数阻塞
e.g. accept input recv - 处理IO的时间较长产生的阻塞状态
e.g. 网络传输,大文件读写
非阻塞IO
代码实现: day11/block_io
- 定义 :通过修改IO属性行为,使原本阻塞的IO变为非阻塞的状态。
- 设置套接字为非阻塞IO
sockfd.setblocking(bool) flase=非阻塞 true=阻塞
功能:设置套接字为非阻塞IO
参数:默认为True,表示套接字IO阻塞;设置为False则套接字IO变为非阻塞
-
超时检测 :设置一个最长阻塞时间,超过该时间后则不再阻塞等待。
sockfd.settimeout(sec)
功能:设置套接字的超时时间
参数:设置的时间
图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jm0HtF0c-1639749684420)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211217093444198.png)]
"""
block_io.py
socket 套接字非阻塞实例
"""
from socket import *
from time import ctime, sleep
# 日志文件
f = open('log.txt', 'a+')
sockfd = socket()
sockfd.bind(('127.0.0.1', 8888))
sockfd.listen(3)
# # 设置套接字为非阻塞
# sockfd.setblocking(False)
# 设置超时检测
sockfd.settimeout(3)
while True:
print('Waiting for connect....')
# 没有客户端连接 每隔3秒写一条日志
try:
connfd, addr = sockfd.accept()
except (BlockingIOError,timeout) as e:
sleep(3)
f.write('%s : %s\n' % (ctime(), e))
f.flush()
else:
print('Connect from', addr)
# recv 还是阻塞状态
# 把 connfd.setblocking(False)
# recv 就变成了 非阻塞
data = connfd.recv(1024).decode()
print(data)
IO多路复用
- 定义
同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。以此形成可以同时处理多个IO的行为,避免一个IO阻塞造成其他IO均无法执行,提高了IO执行效率。
- 具体方案
select方法 : windows linux unix
poll方法: linux unix
epoll方法: linux
select 方法
代码实现: day11/select_server.py
rs, ws, xs=select(rlist, wlist, xlist[, timeout])
功能: 监控IO事件,阻塞等待IO发生
参数:rlist 列表 存放关注的等待发生的IO事件
wlist 列表 存放关注的要主动处理的IO事件
xlist 列表 存放关注的出现异常要处理的IO
timeout 超时时间
返回值: rs 列表 rlist中准备就绪的IO
ws 列表 wlist中准备就绪的IO
xs 列表 xlist中准备就绪的IO
select 实现tcp服务
【1】 将关注的IO放入对应的监控类别列表
【2】通过select函数进行监控
【3】遍历select返回值列表,确定就绪IO事件
【4】处理发生的IO事件
注意
wlist中如果存在IO事件,则select立即返回给ws
处理IO过程中不要出现死循环占有服务端的情况
IO多路复用消耗资源较少,效率较高
图
代码
服务器端
"""
select tcp 服务
重点代码
思路分析:
1. 将关注的IO 放入到监控列表
2. 当IO就绪时会通过select 返回
3. 遍历返回值列表,得知哪个IO就绪进行处理
"""
from socket import *
from select import select
# 创建监听套接字,作为关注的IO
s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 48888))
s.listen(3)
# 设置关注列表
rlist = [s] # s用于等待处理连接
wlist = []
xlist = []
# 循环监控IO
while True:
rs, ws, xs = select(rlist, wlist, xlist)
# 遍历返回值列表,得知哪个IO就绪进行处理
for r in rs:
if r is s:
c, addr = r.accept()
print('Connect from', addr)
rlist.append(c)
else:
# 有客户端发消息
data = r.recv(1024).decode()
if not data:
rlist.remove(r)
r.close()
continue
print(data)
# r.send(b'ok')
wlist.append(r)
for w in ws:
w.send(b'ok')
# 消息发完移除
wlist.remove(w)
for x in xs:
pass
客户端
"""
tcp_client.py tcp 客户端流程
重点代码
"""
from socket import *
# 创建tcp套接字
from time import sleep
sockfd = socket()
# 连接服务器
server_addr = ('127.0.0.1', 48888)
# server_addr = ('119.91.116.248', 48888)
sockfd.connect(server_addr)
for i in range(5):
sleep(2)
sockfd.send(('%s你好'%i).encode())
print(sockfd.recv(1024).decode())
# data = sockfd.recv(1024)
# 关闭套接字
sockfd.close()