IO并发

IO分类:
阻塞IO ,非阻塞IO,IO多路复用,异步IO等。

阻塞IO:
(执行条件不满足)是IO的默认形态。是一种效率十分低的一种IO。

发生阻塞的情况:

  1. 因为某种执行条件没有满足造成的函数阻塞;
  2. 处理IO的时间较长产生的阻塞状态。

非阻塞IO:
通过修改IO属性行为,使原本阻塞的IO变为非阻塞的状态。

设置套接字为非阻塞IO.sockfd.setblocking(bool)
功能:设置套接字为非阻塞IO。
参数:True,表示套接字IO阻塞;设置False则套接字IO变为非阻塞。

超时检测:设置一个最长的阻塞时间,超过时间后则不在阻塞等待。
sockfd.settimeout(sec)
功能:设置套接字的超时间。
参数:设置时间。

代码实现演示:

"""
block_io.py 非阻塞io演示
"""
from socket import *
from time import ctime,sleep

f = open('log.txt','a') # 日志文件

s = socket()
s.bind(('0.0.0.0',8888))
s.listen(5)

# 设置套接字非阻塞
# s.setblocking(False)

# 设置超时时间
s.settimeout(3)

while True:
    try:
        c,addr = s.accept()
        print("Connect from",addr)
    except (BlockingIOError,timeout) as e:
        sleep(2)
        f.write(ctime()+':'+str(e)+'\n')
    else:
        data = c.recv(1024)
        print(data)

IO多路复用:

定义:同时监控多个IO事件,当那个IO事件准备就绪就执行那个IO事件。以此形成可以同时处理多个IO的行为,避免一个IO阻塞造成其他IO无法执行,提高IO执行效率。

实现多路复用的方法有:
select:windows ,linux,unix
poll:Linux,unix
epoll:linux

select方法代码演示:

"""
select 演示
"""

from select import select
from socket import *

s = socket()
s.bind(('127.0.0.1',8888))
s.listen(3)

f = open('12.txt')

print(" 监控io ")
rs,ws,xs = select([s],[s],[s])
print("rlist:",rs)
print("wlist:",ws)
print("xlist:",xs)
"""
select tcp 服务
1.将关注的IO放入对应的监控类别列表
2.通过select函数进行监控
3.遍历select返回值列表,确定就绪IO事件
4.处理发生的IO事件
"""

from socket import *
from select import select

# 创建监听套接字作为关注IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(3)

# 设置关注列表
rlist = [s] # 等待客户端连接
wlist = []
xlist = []

# 监控IO发生
while True:
    rs,ws,xs = select(rlist,wlist,xlist)
    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) # 从写监控中移除

poll方法实现代码演示:

"""
poll  方法实现IO多路服用
【1】 创建套接字
【2】 将套接字register
【3】 创建查找字典,并维护
【4】 循环监控IO发生
【5】 处理发生的IO
"""

from socket import *
from select import *

# 创建套接字作为关注IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(3)

# 创建poll对象
p = poll()
# 建立查找字典,通过IO对象的fileno找到对象
# 字典内容与关注IO保持一直{fileno:io_obj}
fdmap = {s.fileno():s}

# 关注s
p.register(s,POLLIN|POLLERR)

# 循环监控IO的发生
while True:
    events = p.poll()
    print(events)
    for fd,event in events:
        if fd == s.fileno():
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            # 添加新的关注对象,同时维护字典
            p.register(c,POLLIN)
            fdmap[c.fileno()] = c
        elif event & POLLIN:
            data = fdmap[fd].recv(1024).decode()
            if not data:
                # 客户端退出
                p.unregister(fd) # 取消关注
                fdmap[fd].close()
                del fdmap[fd]
                continue
            print(data)
            p.register(fd,POLLOUT)
            # fdmap[fd].send(b'OK')
        elif event & POLLOUT:
            fdmap[fd].send(b'OK')
            p.register(fd, POLLIN)

p.register(fd,event):

  1. 功能: 注册关注的IO事件
  2. 参数:fd 要关注的IO
    【1】. event 要关注的IO事件类型
    【2】. 常用类型:
    POLLIN 读IO事件(rlist)
    POLLOUT 写IO事件 (wlist)
    POLLERR 异常IO (xlist)
    POLLHUP 断开连接
    e.g. p.register(sockfd,POLLIN|POLLERR)

p.unregister(fd):

  1. 功能:取消对IO的关注
  2. 参数:IO对象或者IO对象的fileno

events = p.poll():

  1. 功能: 阻塞等待监控的IO事件发生
  2. 返回值: 返回发生的IO
    【1】events格式 [(fileno,event),()…]
    【2】每个元组为一个就绪IO,元组第一项是该IO的fileno,第二项为该IO就绪的事件类型。

epoll方式实现代码:

"""
epoll  方法实现IO多路服用
【1】 创建套接字
【2】 将套接字register
【3】 创建查找字典,并维护
【4】 循环监控IO发生
【5】 处理发生的IO
"""

from socket import *
from select import *

# 创建套接字作为关注IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('127.0.0.1',8888))
s.listen(3)

# 创建epoll对象
ep = epoll()

# 建立查找字典,通过IO对象的fileno找到对象
# 字典内容与关注IO保持一直{fileno:io_obj}
fdmap = {s.fileno():s}

# 关注s
ep.register(s,EPOLLIN|EPOLLERR)

# 循环监控IO的发生
while True:
    events = ep.poll()
    print("你有新的IO需要处理哦:",events)
    for fd,event in events:
        if fd == s.fileno():
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            # 添加新的关注对象,同时维护字典
            ep.register(c,EPOLLIN|EPOLLET) # 边缘触发
            fdmap[c.fileno()] = c
        elif event & EPOLLIN:
            data = fdmap[fd].recv(1024).decode()
            if not data:
                # 客户端退出
                ep.unregister(fd) # 取消关注
                fdmap[fd].close()
                del fdmap[fd]
                continue
            print(data)
            ep.unregister(fd)
            ep.register(fd, POLLOUT)
        elif event & POLLOUT:
            fdmap[fd].send(b'OK')
            ep.unregister(fd)
            ep.register(fd, POLLIN)

epoll特点:

  1. epoll 效率比select poll要高
  2. epoll 监控IO数量比select要多
  3. epoll 的触发方式比poll要多 (EPOLLET边缘触发)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值