IO:即Input/Output,凡是在内存中存在数据交换的操作都可以认为是IO操作。IO密集型程序:程序执行中执行大量的IO操作,而只有较少的cpu运算;消耗计算机资源较少,运行时间长。注:CPU密集型程序(计算密集型): 程序运行中需要大量的cpu运算,IO操作较少;消耗cpu资源多,运行速度快。
举例:
内存和磁盘交互 文件读写(read、write)
内存和终端交互 输入打印(input、print)
内存和网路交互 发送接收(send、recv)
IO操作的分类 :阻塞IO、非阻塞IO、IO多路复用、事件IO、异步IO
阻塞IO是IO操作的默认形态,也是效率最低的一种IO操作。
阻塞IO形成原因:因为等待某种条件达成才能继续运行,eg:accept、recv、input等;处理IO事件的时候耗时较长也会产生阻塞,eg:文件的读写过程,网络数据的传输过程等。
非阻塞IO通常是通过修改IO事件的属性,使其变为非阻塞状态,即避免条件阻塞的情况。
非阻塞IO往往和循环搭配使用,这样可以不断执行部分需要执行的代码,也不影响对阻塞条件的判断;虽需要消耗更多cpu但是一定程度上提高了IO效率
参数 : 设置为False则套接字调用函数为非阻塞
通过设置超时检测使其成为非阻塞程序,即将原本阻塞的IO设置一个最长阻塞等待时间,在规定时间内如果达到条件则正常执行,如果时间到仍未达到条件则结束阻塞。
s.settimeout(sec)
功能 : 设置套接字超时时间
参数 : 设置的时间
from socket import *
from time import sleep,ctime
s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置s是非阻塞状态
s.setblocking(False)
while True:
print("waiting for connect....")
try:
connfd,addr = s.accept()
except BlockingIOError:
sleep(2)
print(ctime())
continue
print("connect from",addr)
# recv变为非阻塞
# connfd.setblocking(False)
while True:
data = connfd.recv(1024)
if not data:
break
print(data.decode())
connfd.send('来,确认下眼神'.encode())
connfd.close()
s.close()
from socket import *
from time import sleep,ctime
s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置超时等待时间 5秒
s.settimeout(5)
while True:
print("Waiting for connect...")
try:
connfd,addr = s.accept()
except timeout:
print("time out .....")
sleep(2)
print(ctime())
continue
print("Connect from",addr)
while True:
data = connfd.recv(1024).decode()
if not data:
break
print(data)
connfd.sendall(ctime().encode())
connfd.close()
s.close()
IO多路复用指同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。此时形成多个IO事件都可以操作的现象,不必逐个等待执行。
IO多路复用处理IO的过程中不应有死循环出现,使一个客户端长期占有服务端; IO多路复用是一种并发行为,但是是单进程程序,有较高的IO执行效率。
IO多路复用函数有select、poll和epoll等函数,需要引入select模块(import select)。select函数适用于用于windows、linux和unix;poll和epoll函数适用于用于linux和unix。
select、poll和epoll的 区别:
1. select可以很好支持windows;
2. epoll比select和poll效率高,select和poll差不多;
3. epoll提供了更多的触发方式(epoll支持边缘触发 select poll只支持水平触发)。
r, w, x = select(rlist, wlist, xlist[, timeout])
功能: 监控IO事件,阻塞等待IO事件发生
参数: rlist 列表 存放被动等待处理的IO事件
wlist 列表 存放需要主动处理的IO
xlist 列表 存放如果发生异常需要处理的IO
timeout 数字 超时监测 默认一直阻塞
返回值 : r 列表 rlist中准备就绪的IO
w 列表 wlist中准备就绪的IO
x 列表 xlist中准备就绪的IO
from select import poll
p = poll()
poll的IO 事件分类:
POLLIN POLLOUT POLLERR POLLHUP POLLPRI POLLVAL
rlist wlist xlist 断开连接 紧急处理 无效
p.register(s,POLLIN | POLLERR)
p.unregister(s) # 取消对IO的关注
events = p.poll()
功能 : 监控关注的IO,阻塞等待IO发生
返回值 : events是一个列表,列表中每个元素为一个元组,代表准备就绪需要处理的IO。
events --》 [( fileno, event),(),()]
就绪IO的fileno 哪个事件就绪
因为要获取IO对象调用函数---》通过fileno得到对象
实施方法 : 建立比照字典 {s.fileno():s}
使用方法 : 与poll基本相同
生成对象使用epoll() 而不是poll() ,但是监控还是poll()
register注册IO事件事件类型改为epoll事件类型
from socket import *
from select import *
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",8888))
s.listen(5)
#创建POLL对象
p = poll()
#建立通过fileno查找IO对象的字典
fdmap = {s.fileno():s}
#注册关注IO事件
p.register(s,POLLIN | POLLERR)
while True:
#监控关注的IO
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("Connect from",addr)
#注册新的关注IO
p.register(c,POLLIN | POLLERR)
#维护字典
fdmap[c.fileno()] = c
elif event & POLLIN:
data = fdmap[fd].recv(1024)
if not data:
#客户端退出,取消关注,维护字典
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send('收到了'.encode())
from socket import *
from select import *
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",8888))
s.listen(5)
#创建EPOLL对象
p = epoll()
#建立通过fileno查找IO对象的字典
fdmap = {s.fileno():s}
#注册关注IO事件
p.register(s,EPOLLIN | EPOLLERR)
while True:
#监控关注的IO
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("Connect from",addr)
#注册新的关注IO
p.register(c,EPOLLIN | EPOLLERR)
#维护字典
fdmap[c.fileno()] = c
elif event & POLLIN:
data = fdmap[fd].recv(1024)
if not data:
#客户端退出,取消关注,维护字典
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send('收到了'.encode())
人工智能(PythonNet)—— 目录汇总