select --- 等待一组流上的事件

此模块提供了一些函数,可高效地等待一个或多个 (实现流协议的类文件对象,如套接字、UART 和其他 I/O 对象)准备好读取或写入,而不必忙等待或在单个对象上阻塞。

poll 对象是推荐的接口:它可扩展到许多流,并且在通过 poll.ipoll() 使用时无需分配内存。模块级的 select() 函数则是一个效率较低的兼容性接口。

使用 poll 对象的示例:

import select

poller = select.poll()
poller.register(uart, select.POLLIN)

while True:
    # Wait up to 1000 ms for the UART to have data.
    for obj, event in poller.poll(1000):
        if event & select.POLLIN:
            print(obj.read())

使用模块级 select() 函数的示例:

import select

# Wait up to 1 second for the socket to become readable.
rlist, wlist, xlist = select.select([sock], [], [], 1.0)
if rlist:
    data = sock.recv(100)

函数

select.select(rlist: List, wlist: List, xlist: List, timeout: float | None = None) Tuple[List, List, List]

等待直到一个或多个给定的流对象准备就绪,或直到 timeout 到期。

  • rlist 是要监视其可读性的对象列表。

  • wlist 是要监视其可写性的对象列表。

  • xlist 是要监视其错误或挂断状况的对象列表。

  • timeout 是最大等待时间,以秒为单位(接受浮点数)。如果省略或为 None,则调用无限期阻塞;0 表示立即返回(非阻塞轮询)。

返回一个由列表组成的三元组 (rlist, wlist, xlist)。每个返回的列表是对应输入列表的子集,分别包含已准备好读取、已准备好写入或发出错误/挂断信号的对象。如果 timeout 已过但没有任何对象就绪,则三个列表均为空。

此函数的效率低于 poll(它在每次调用时都会重建其内部轮询集);请尽可能改用 poll

常量

select.POLLIN: int

流中有可读取的数据。

select.POLLOUT: int

流可以接受更多要写入的数据。

select.POLLERR: int

流上发生了错误状况。这是一个非请求事件:即使未在 eventmask 中请求,它也会由 poll.poll() / poll.ipoll() 报告,并且将其作为输入 eventmask 传入是无效的。

select.POLLHUP: int

流已挂断/断开连接。这是一个非请求事件:即使未在 eventmask 中请求,它也会由 poll.poll() / poll.ipoll() 报告,并且将其作为输入 eventmask 传入是无效的。

class select.poll

创建一个轮询对象,它维护一组已注册的流(或任何暴露流协议的对象),并高效地等待,直到其中一个或多个变为可读、可写或发出异常状况信号。

register() 添加流,用 unregister() 移除流,并且可用 modify() 更改要监视的事件集。配置完成后,调用 poll() 进行阻塞,直到有内容就绪(或超时到期),或调用 ipoll() 获取一个无需分配内存的基于迭代器的变体。

register(obj: Any, eventmask: int = select.POLLIN | select.POLLOUT) None

注册 obj 以进行轮询,监视由 eventmask(以下各项的逻辑或)给定的事件:

eventmask 默认为 select.POLLIN | select.POLLOUT

select.POLLHUPselect.POLLERR 在输入 eventmask无效——它们是非请求事件,无论是否被请求,都会由 poll() / ipoll() 报告(这与 POSIX 语义一致)。

对同一个 obj 多次调用此方法是允许的:后续调用会更新 obj 的事件掩码,其行为类似于 modify()

unregister(obj: Any) None

从已注册的流集中移除 obj。注销一个当前未注册的 obj 不算错误(在这种情况下调用没有任何效果)。

modify(obj: Any, eventmask: int) None

将一个已注册的 obj 的事件掩码更改为 eventmask。如果 obj 未注册,则引发带有 errno.ENOENTOSError

poll(timeout: int = -1, /) List[Tuple]

阻塞,直到至少一个已注册的流准备就绪或发出异常状况信号,然后返回触发的流的列表。

timeout 是最大等待时间,以毫秒为单位。如果省略或为 -1,则调用无限期阻塞;0 表示立即返回(非阻塞轮询)。

返回一个 (obj, event, ...) 元组的列表,每个触发的流对应一个。obj 是已注册的流,event 是发生的 select.POLLIN / select.POLLOUT / select.POLLERR / select.POLLHUP 标志的按位或。每个元组可能包含额外的、由实现定义的元素,因此不要假设其长度恰好为 2。如果 timeout 已过但没有任何对象就绪,则返回一个空列表。

select.POLLHUPselect.POLLERR 可能在任何时候返回(即使未被请求),并且必须加以处理——通常是注销并关闭受影响的流——否则后续调用将持续立即返回,并为该流设置这些标志。

任何待处理的已调度回调都保证在进入轮询循环之前运行。

与 CPython 的差异

如上所述,返回的元组可能包含超过 2 个元素。

ipoll(timeout: int = -1, flags: int = 0, /) Iterator[Tuple]

poll() 类似,但不构建列表,而是返回一个迭代器,每次产生一个 (obj, event, ...) 元组。这避免了在每次调用时分配内存,这对于异步 I/O 调度器非常重要。

产生的元组归被调用方所有:它会在下一次迭代时被复用(覆盖),因此必须在循环体内消费其内容,并且不得存储对它的引用。

timeout 的含义与 poll() 相同。如果 flags1,则使用单次(one-shot)行为:事件已触发的流会自动清除其事件掩码(等效于 poll.modify(obj, 0)),因此在用 modify() 再次设置其掩码之前,不会再为其报告任何事件。

任何待处理的已调度回调都保证在进入轮询循环之前运行。

与 CPython 的差异

此方法是 MicroPython 的扩展。