参考自:selectors — High-level I/O multiplexing
selectors
模块允许高层级且高效率的 I/O 复用,它建立在 select
模块原型的基础之上,提供基于 select
模块的 I/O 复用的平台无关的抽象。推荐使用 selectors
模块,除非希望对所使用的 OS 层级原型进行精确控制。它定义了一个 BaseSelector
抽象基类,以及多个实际的实现 (KqueueSelector
, EpollSelector
…),它们可被用于在多个文件对象上等待 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
,调用将不会阻塞,并将报告当前就绪的文件对象。如果timeout
为None
,调用将阻塞直到某个被监视的文件对象就绪。 - 返回由
(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连接对象