高级 I/O 复用库


参考自:selectors — High-level I/O multiplexing

selectors 模块允许高层级且高效率的 I/O 复用,它建立在 select 模块原型的基础之上,提供基于 select 模块的 I/O 复用的平台无关的抽象。推荐使用 selectors 模块,除非希望对所使用的 OS 层级原型进行精确控制。它定义了一个 BaseSelector 抽象基类,以及多个实际的实现 (KqueueSelectorEpollSelector…),它们可被用于在多个文件对象上等待 I/O 就绪通知。 在下文中,”文件对象” 是指任何具有 fileno() 方法的对象,或是一个原始文件描述器。参见 file object

文件对象(file object):对外提供面向文件 API 以使用下层资源的对象(带有 read()write() 这样的方法)。根据其创建方式的不同,文件对象可以处理对真实磁盘文件,对其他类型存储,或是对通讯设备的访问(例如标准输入/输出、内存缓冲区、套接字、管道等等)。文件对象也被称为 file-like objects(streams)。

实际上共有三种类别的文件对象: 原始 二进制文件, 被缓冲的 二进制文件 以及 文本文件。它们的接口定义均在 io 模块中。创建文件对象的规范方式是使用 open() 函数。

DefaultSelector 是一个指向当前平台上可用的最高效实现的别名:这应为大多数用户的默认选择。

if _can_use('kqueue'):
    DefaultSelector = KqueueSelector
elif _can_use('epoll'):
    DefaultSelector = EpollSelector
elif _can_use('devpoll'):
    DefaultSelector = DevpollSelector
elif _can_use('poll'):
    DefaultSelector = PollSelector
else:
    DefaultSelector = SelectSelector

下文中,events 一个位掩码,指明哪些 I/O 事件要在给定的文件对象上执行等待。它可以是以下模块级常量的组合:

常量 解释
EVENT_READ 可读
EVENT_WRITE 可写

class selectors.SelectorKey

SelectorKey 是一个 namedtuple,用来将文件对象关联到其隐含的文件描述器、选定事件掩码和附加数据等。它会被某些 BaseSelector 方法返回。

fileobj
已注册的文件对象
fd
隐含的的文件描述器(Underlying file descriptor)
events
必须在此文件对象上被等待的事件
data
可选的关联到此文件对象的不透明数据:例如,这可被用来存储各个客户端的会话 ID

class selectors.BaseSelector

一个 BaseSelector,用来在多个文件对象上等待 I/O 事件就绪。它支持文件流注册、注销,以及在这些流上等待 I/O 事件的方法。它是一个抽象基类,因此不能被实例化。请改用 DefaultSelector,或者 SelectSelector, KqueueSelector 等。如果你想要指明使用某个实现,并且你的平台支持它的话。BaseSelector 及其具体实现支持 context manager 协议。

abstractmethod register(fileobj, events, data=None)
注册一个用于选择的文件对象,在其上监视 I/O 事件。
fileobj 是要监视的文件对象。它可以是整数形式的文件描述符或者具有 fileno() 方法的对象。events 是要监视的事件的位掩码。data 是一个不透明对象。
这将返回一个新的 SelectorKey 实例,或在出现无效事件掩码或文件描述符时引发 ValueError,或在文件对象已被注册时引发 KeyError
abstractmethod unregister(fileobj)
注销对一个文件对象的选择,移除对它的监视。在文件对象被关闭之前应当先将其注销。
fileobj必须是之前已注册的文件对象。
这将返回已关联的 SelectorKey 实例,或者如果 fileobj 未注册则会引发 KeyError。 如果 fileobj 无效(例如它没有 fileobj() 方法或其 fileobj() 方法返回无效值),则返回 ValueError
modify(fileobj, events, data=None)(fileobj)
更改已注册文件对象所监视的事件或所附带的数据。
这等价于 BaseSelector.unregister(fileobj)()BaseSelector.register(fileobj, events, data)(),区别在于它可以被更高效地实现。
这将返回一个新的 SelectorKey 实例,或在出现无效事件掩码或文件描述符时引发 ValueError,或在文件对象未被注册时引发 KeyError
abstractmethod select(timeout=None)
等待直到有已注册的文件对象就绪,或是超过时限。
如果 timeout > 0,这指定以秒数表示的最大等待时间。如果 timeout <= 0,调用将不会阻塞,并将报告当前就绪的文件对象。如果 timeoutNone,调用将阻塞直到某个被监视的文件对象就绪。
返回由 (key, events) 元组构成的列表,每项各表示一个就绪的文件对象。
key 是对应于就绪文件对象的 SelectorKey 实例。events 是在此文件对象上等待的事件位掩码。
注解:如果当前进程收到一个信号(signal),此方法可在任何文件对象就绪之前或超出时限时返回:在此情况下,将返回一个空列表。
在 3.5 版更改: 现在当被某个信号中断时,如果信号处理程序没有引发异常,选择器会用重新计算的超时值进行重试(理由请查看 PEP 475 ),而不是在超时之前返回空的事件列表。
close()
关闭选择器(selector)。
必须调用这个方法以确保下层资源会被释放。选择器被关闭后将不可再使用。
get_key(fileobj)
返回关联到某个已注册文件对象的键。
此方法将返回关联到文件对象的 SelectorKey 实例,或在文件对象未注册时引发 KeyError
abstractmethod get_map()
返回从文件对象到选择器键的映射。
返回一个将已注册文件对象映射到与其相关联的SelectorKey 实例的 Mapping 实例。

一个示例

下面是一个简单的echo服务器实现:

import selectors
import socket
# 生成一个 select 对象
sel = selectors.DefaultSelector()

def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    print('accepted', conn, 'from', addr)
    conn.setblocking(False) # 设定非阻塞
    # 新连接注册 read 回调函数
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print('echoing', repr(data), 'to', conn)
        conn.send(data)  # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()

sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
# 把刚生成的sock连接对象注册到select连接列表中,并交给accept函数处理
sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select() # 默认是阻塞,有活动连接就返回活动的连接列表
    for key, mask in events:
        callback = key.data # 去调accept函数
        callback(key.fileobj, mask)  # key.fileobj就是readable中的一个socket连接对象

https://pymotw.com/3/selectors/


文章作者: xinetzone
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xinetzone !
评论
  目录