18.5.1. 基本事件循环

事件循环是 asyncio 提供的核心运行机制。它提供多个功能,包括︰

  • 注册、 执行和取消延迟调用 (超时)。
  • 为多种通讯方式创建客户端和服务端 通信
  • 为了与外部程序进行通讯会启动子进程和相关的 运输协议
  • 将耗时的函数调用委派给线程池
class asyncio.BaseEventLoop

此类是实现细节。它是 AbstractEventLoop 的一个子类,也是在 asyncio 中的具体事件循环实现的基类。不应直接使用; 而应该使用 AbstractEventLoop 来代替。BaseEventLoop 不应该被第三方继承; 因为其类内部接口不是稳定的。

class asyncio.AbstractEventLoop

事件循环的基类

此类是 线程不安全

18.5.1.1. 运行事件循环

AbstractEventLoop.run_forever()

运行,直到调用 stop()如果 stop()run_forever()调用之前被调用,事件轮询一次将超时时间设置为0,所有预先安排好的响应 I/O 事件 的回调(和那些已经预定)将会立即执行,然后退出。如果 run_forever() 正在运行的时候,调用 stop(),这将运行当前正在处理的事件的回调,然后退出。注意,回调调度的回调在这种情况下不会运行;它们将在下一次运行run_forever()时调用。

在版本3.5.1中更改。

AbstractEventLoop.run_until_complete(future)

运行Future对象直到其完成 。

如果参数是一个 协程对象,它是由 ensure_future() 包装。

返回的Future对象的执行结果,或引发的异常。

AbstractEventLoop.is_running()

返回事件循环的运行状态。

AbstractEventLoop.stop()

停止运行事件循环。

这会使调用了 run_forever()的事件循环,会在下一个合适的机会退出 (具体参考run_forever()当中描述的更多细节)。

在版本3.5.1中更改。

AbstractEventLoop.is_closed()

如果事件循环被关闭,则返回 True

版本3.4.2中的新功能。

AbstractEventLoop.close()

关闭事件循环。该循环不能被运行。挂起的回调将会丢失。

这会清除队列并关闭执行程序,但不会等待执行程序完成。

这是幂等和不可逆转。这将是最后一个调用的方法。

18.5.1.2. 调用

大多数的 asyncio 函数不接受关键字参数。如果你想要传递关键字参数给回调,使用 functools.partial()例如, loop.call_soon(functools.partial(print, "Hello", flush=True)) 将调用 print("Hello", flush=True).

注意

functools.partial()lambda 函数更好,因为 asyncio 可以在调试模式下检查传递给functools.partial() 对象的显示参数,而lambda 函数则是一个不够好的代表。

AbstractEventLoop.call_soon(callback, *args)

安排一个callback被尽快调用。当控制返回到事件循环时,如call_soon() 已返回,指定的callback即被调用。

这个操作是类似 FIFO 队列,callbacks按照它们注册的顺序回调。每个回调将被调用一次。

任何位置参数将在回调执行时被传入。

返回一个 asyncio.Handle的实例 ,可以用于取消回调。

Use functools.partial to pass keywords to the callback

AbstractEventLoop.call_soon_threadsafe(callback, *args)

功能与call_soon()一致,但线程安全。

请参阅文档的concurrency and multithreading部分。

18.5.1.3. 延迟调用

事件循环拥有属于自己的计时器来计算延时.使用哪种计时器取决于不同的操作系统提供的事件循环实现.这通常是与time.time()不同的计时器。

注意

超时(相对延迟或绝对when)不应超过一天。

AbstractEventLoop.call_later(delay, callback, *args)

安排在给定的延迟秒数(int或float)后调用回调函数

返回一个asyncio.Handle的实例,可用于取消回调。

call_later()的每次执行只会调用回调函数一次。如果两个回调被安排调度的时间完全相同,哪一个被先调用是不明确的。

可选的位置args将在调用时传递给回调函数。如果你希望回调函数执行时传入命名参数,请使用闭包或functools.partial()

Use functools.partial to pass keywords to the callback

AbstractEventLoop.call_at(when, callback, *args)

安排 callback 在给定的绝时时间when(an int或float)被调用, 使用的时间引用与AbstractEventLoop.time()相同.

此方法的行为与call_later()相同。

返回asyncio.Handle的实例,可用于取消回调。

Use functools.partial to pass keywords to the callback

AbstractEventLoop.time()

根据事件循环的内部时钟,将当前时间作为float值返回。

参见

asyncio.sleep()函数。

18.5.1.4. Futures

AbstractEventLoop.create_future()

创建 asyncio.Future 对象附加到该循环。

这是在asyncio中创建Futures的首选方式,因为事件循环实现能提供Future类的(具有更好的性能或更方便的实现方式的)替代实现。

版本3.5.2中的新功能。

18.5.1.5. 任务¶ T0>

AbstractEventLoop.create_task(coro)

安排协程对象的执行:用一个future封装它。返回Task对象。

第三方事件循环可以使用自己的Task子类来实现互操作性。在这种情况下,结果类型是Task的子类。

这个方法是在Python 3.4.2中添加的。使用async()函数也支持旧的Python版本。

版本3.4.2中的新功能。

AbstractEventLoop.set_task_factory(factory)

设置将由AbstractEventLoop.create_task()使用的任务工厂。

如果factoryNone,则将设置默认任务工厂。

如果factory可调用,则它应该具有与loop,coro匹配的声明,其中loop将是对活动事件循环的引用,coro将是协程对象。可调用项必须返回asyncio.Future兼容对象。

版本3.4.4中的新功能。

AbstractEventLoop.get_task_factory()

返回任务工厂,或None(如果使用默认工厂)。

版本3.4.4中的新功能。

18.5.1.6. 创建连接

coroutine AbstractEventLoop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None)

根据创建到给定Internet 主机端口:套接字系列AF_INETAF_INET6 > host(或family,如果指定),套接字类型SOCK_STREAMprotocol_factory必须是可返回protocol的可调用实例。

此方法是coroutine,它将尝试在后台建立连接。成功时,协程返回(transport, 协议)对。

底层操作的时间概要如下:

  1. 建立连接,并创建transport以表示它。
  2. protocol_factory无参数调用,必须返回protocol实例。
  3. 协议实例绑定到传输,并且它的connection_made()方法被调用。
  4. 协程成功返回(transport, protocol)对。

创建的传输是依赖于实现的双向流。

注意

protocol_factory可以是任何类型的可调用,不一定是类。例如,如果要使用预创建的协议实例,可以传递lambda: my_protocol

更改连接创建方式的选项:

  • ssl:如果给定且不为false,则创建SSL / TLS传输(默认情况下创建纯TCP传输)。如果sslssl.SSLContext对象,则此上下文用于创建传输;如果sslTrue,则使用具有某些未指定的默认设置的上下文。

  • server_hostname仅与ssl一起使用,并设置或覆盖与目标服务器的证书匹配的主机名。默认情况下,使用host参数的值。如果host为空,则没有默认值,您必须传递server_hostname的值。如果server_hostname是空字符串,则禁用主机名匹配(这是一个严重的安全风险,允许中间人攻击)。

  • family, proto, flags are the optional address family, protocol and flags to be passed through to getaddrinfo() for host resolution. 如果给定,这些都应该是相应的socket模块常量的整数。

  • sock(如果给定)应为现有的已连接的socket.socket对象,供传输使用。如果给出sock,则主机端口系列proto应指定标志local_addr

  • local_addr(如果给出)是用于将套接字本地绑定的(local_host, local_port) 主机端口类似,使用getaddrinfo()查找local_hostlocal_port

在版本3.5中已更改:在Windows上使用ProactorEventLoop,现在支持SSL / TLS。

也可以看看

open_connection()函数可用于获取一对(StreamReaderStreamWriter)而不是协议。

coroutine AbstractEventLoop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)

Create datagram connection: socket family AF_INET or AF_INET6 depending on host (or family if specified), socket type SOCK_DGRAM. protocol_factory必须是可返回protocol的可调用实例。

此方法是coroutine,它将尝试在后台建立连接。成功时,协程返回(transport, 协议)对。

更改连接创建方式的选项:

  • local_addr(如果给出)是用于将套接字本地绑定的(local_host, local_port) 使用getaddrinfo()查找local_hostlocal_port
  • remote_addr(如果给出)是用于将套接字连接到远程地址的(remote_host, remote_port) 。使用getaddrinfo()查找remote_hostremote_port
  • 家庭proto标志是可传递到getaddrinfo()用于主机分辨率。如果给定,这些都应该是相应的socket模块常量的整数。
  • reuse_address告诉内核重用TIME_WAIT状态中的本地套接字,而不必等待其自然超时到期。如果未指定,将在UNIX上自动设置为True。
  • reuse_port告诉内核允许此端点绑定到与其他现有端点绑定的端口相同的端口,只要它们在创建时都设置此标志。此选项在Windows和某些UNIX上不受支持。如果未定义SO_REUSEPORT常量,则不支持此功能。
  • allow_broadcast告诉内核允许此端点向广播地址发送消息。
  • 可以可选地指定sock,以便使用先前存在的,已连接的socket.socket对象以供传输使用。如果指定,应省略local_addrremote_addr(必须None)。

在Windows上使用ProactorEventLoop,不支持此方法。

请参见UDP echo client protocolUDP echo server protocol示例。

coroutine AbstractEventLoop.create_unix_connection(protocol_factory, path, *, ssl=None, sock=None, server_hostname=None)

创建UNIX连接:套接字系列AF_UNIX,套接字类型SOCK_STREAMAF_UNIX套接字系列用于高效地在同一机器上的进程之间进行通信。

此方法是coroutine,它将尝试在后台建立连接。成功时,协程返回(transport, protocol)对。

请参阅参数的AbstractEventLoop.create_connection()方法。

可用性:UNIX。

18.5.1.7. 创建监听连接

coroutine AbstractEventLoop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None)

创建绑定到主机端口的TCP服务器(套接字类型SOCK_STREAM)。

返回Server对象,其sockets属性包含创建的套接字。使用Server.close()方法停止服务器:关闭侦听套接字。

参数:

  • host参数可以是字符串,在这种情况下,TCP服务器绑定到主机端口host参数也可以是字符串序列,在这种情况下,TCP服务器绑定到序列的所有主机。如果host是空字符串或None,则假定所有接口,并且将返回多个套接字的列表(很可能一个用于IPv4,另一个用于IPv6)。
  • family可以设置为socket.AF_INETAF_INET6,以强制套接字使用IPv4或IPv6。如果未设置,则将从主机确定(默认为socket.AF_UNSPEC)。
  • flagsgetaddrinfo()的位掩码。
  • sock可以指定为了使用一个预先存在的套接字对象。如果指定,应省略主机端口(必须None)。
  • backlog是传递到listen()(默认值为100)的排队连接的最大数量。
  • ssl可以设置为SSLContext,以对接受的连接启用SSL。
  • reuse_address告诉内核重用TIME_WAIT状态中的本地套接字,而不必等待其自然超时到期。如果未指定,将在UNIX上自动设置为True。
  • reuse_port告诉内核允许此端点绑定到与其他现有端点绑定的端口相同的端口,只要它们在创建时都设置此标志。Windows不支持此选项。

此方法是coroutine

在版本3.5中已更改:在Windows上使用ProactorEventLoop,现在支持SSL / TLS。

也可以看看

函数start_server()创建一个(StreamReaderStreamWriter)对,并使用此对调用函数。

在版本3.5.1中更改: t> 主机参数现在可以是字符串序列。

coroutine AbstractEventLoop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None)

类似于AbstractEventLoop.create_server(),但特定于套接字族AF_UNIX

此方法是coroutine

可用性:UNIX。

18.5.1.8. 观察文件描述符

在Windows上使用SelectorEventLoop,只支持套接字句柄(例如:不支持管道文件描述器)。

在Windows上使用ProactorEventLoop,不支持这些方法。

AbstractEventLoop.add_reader(fd, callback, *args)

开始观察文件描述器读取可用性,然后调用具有指定参数的回调

Use functools.partial to pass keywords to the callback

AbstractEventLoop.remove_reader(fd)

停止观看文件描述器读取可用性。

AbstractEventLoop.add_writer(fd, callback, *args)

开始观察文件描述器的写可用性,然后调用具有指定参数的回调

Use functools.partial to pass keywords to the callback

AbstractEventLoop.remove_writer(fd)

停止观看文件描述器写入可用性。

watch a file descriptor for read events示例使用低级AbstractEventLoop.add_reader()方法注册套接字的文件描述器。

18.5.1.9. 低级套接字操作

coroutine AbstractEventLoop.sock_recv(sock, nbytes)

从套接字接收数据。阻塞socket.socket.recv()方法后建模。

返回值是表示接收到的数据的字节对象。一次接收的最大数据量由nbytes指定。

使用SelectorEventLoop事件循环,套接字sock必须是非阻塞的。

此方法是coroutine

coroutine AbstractEventLoop.sock_sendall(sock, data)

将数据发送到套接字。阻塞socket.socket.sendall()方法后建模。

插座必须连接到远程插座。此方法继续从数据发送数据,直到所有数据已发送或发生错误。None成功返​​回。出现错误时,会引发异常,并且无法确定连接的接收端成功处理了多少数据(如果有的话)。

使用SelectorEventLoop事件循环,套接字sock必须是非阻塞的。

此方法是coroutine

coroutine AbstractEventLoop.sock_connect(sock, address)

地址连接到远程套接字。阻塞socket.socket.connect()方法后建模。

使用SelectorEventLoop事件循环,套接字sock必须是非阻塞的。

此方法是coroutine

在3.5.2版更新: address不再需要解决。sock_connect将尝试通过调用socket.inet_pton()来检查地址是否已解决。如果不是,则AbstractEventLoop.getaddrinfo()将用于解析地址

coroutine AbstractEventLoop.sock_accept(sock)

接受连接。阻塞socket.socket.accept()后建模。

套接字必须绑定到地址并侦听连接。返回值是一对(conn, 地址)其中conn新 t4 > socket对象可用于在连接上发送和接收数据,address是连接另一端的套接字的地址。

套接字套接字必须是非阻塞的。

此方法是coroutine

18.5.1.10. 解析主机名

coroutine AbstractEventLoop.getaddrinfo(host, port, *, family=0, type=0, proto=0, flags=0)

此方法是coroutine,类似于socket.getaddrinfo()函数,但不阻塞。

coroutine AbstractEventLoop.getnameinfo(sockaddr, flags=0)

此方法是coroutine,类似于socket.getnameinfo()函数,但不阻塞。

18.5.1.11. 连接管道

在Windows上使用SelectorEventLoop,不支持这些方法。使用ProactorEventLoop支持Windows上的管道。

coroutine AbstractEventLoop.connect_read_pipe(protocol_factory, pipe)

在事件循环中注册读取管道。

protocol_factory应使用Protocolpipe是一个file-like object返回对(传输, 协议),其中传输支持ReadTransport接口。

使用SelectorEventLoop事件循环,管道设置为非阻塞模式。

此方法是coroutine

coroutine AbstractEventLoop.connect_write_pipe(protocol_factory, pipe)

在eventloop中注册写入管道。

protocol_factory应使用BaseProtocol接口实例化对象。pipefile-like object返回对(传输, 协议),其中传输支持WriteTransport接口。

使用SelectorEventLoop事件循环,管道设置为非阻塞模式。

此方法是coroutine

18.5.1.12. UNIX信号

可用性:仅UNIX。

AbstractEventLoop.add_signal_handler(signum, callback, *args)

为信号添加处理程序。

如果信号编号无效或不可复位,则引发ValueError如果设置处理程序时出现问题,则引发RuntimeError

Use functools.partial to pass keywords to the callback

AbstractEventLoop.remove_signal_handler(sig)

删除信号的处理程序。

返回True如果删除了信号处理程序,False如果没有。

也可以看看

signal模块。

18.5.1.13. 执行¶ T0>

调用执行程序(线程池或进程池)中的函数。默认情况下,事件循环使用线程池执行器(ThreadPoolExecutor)。

coroutine AbstractEventLoop.run_in_executor(executor, func, *args)

安排在指定的执行器中调用func

executor参数应为Executor实例。如果executorNone,则使用默认执行程序。

Use functools.partial to pass keywords to the *func*

此方法是 coroutine

AbstractEventLoop.set_default_executor(executor)

设置run_in_executor()使用的默认执行程序。

18.5.1.14. 错误处理API

允许定制在事件循环中如何处理异常。

AbstractEventLoop.set_exception_handler(handler)

处理程序设置为新的事件循环异常处理程序。

如果处理程序None,则将设置默认异常处理程序。

If handler is a callable object, it should have a matching signature to (loop, context), where loop will be a reference to the active event loop, context will be a dict object (see call_exception_handler() documentation for details about context).

AbstractEventLoop.get_exception_handler()

返回异常处理程序,或None(如果使用默认的处理程序)。

版本3.5.2中的新功能。

AbstractEventLoop.default_exception_handler(context)

默认异常处理程序。

当异常发生并且没有异常处理程序被设置时,这被调用,并且可以由想要推迟到默认行为的自定义异常处理程序调用。

上下文参数具有与call_exception_handler()中相同的含义。

AbstractEventLoop.call_exception_handler(context)

调用当前事件循环异常处理程序。

contest是包含以下键(以后可能会引入新键)的dict对象:

  • 'message':错误消息;
  • 'exception'(可选):异常对象;
  • 'future'(可选):asyncio.Future实例;
  • 'handle'(可选):asyncio.Handle实例;
  • 'protocol'(可选):Protocol实例;
  • 'transport'(可选):Transport实例;
  • 'socket'(可选):socket.socket实例。

注意

注意:此方法不应在子类事件循环中重载。对于任何自定义异常处理,请使用set_exception_handler()方法。

18.5.1.15. 调试模式

AbstractEventLoop.get_debug()

获取事件循环的调试模式(bool)。

如果环境变量 PYTHONASYNCIODEBUG设置为非空字符串,则默认值为True,否则为False

版本3.4.2中的新功能。

AbstractEventLoop.set_debug(enabled: bool)

设置事件循环的调试模式。

版本3.4.2中的新功能。

也可以看看

asyncio的debug mode of asyncio

18.5.1.16. 服务器¶ T0>

class asyncio.Server

服务器侦听套接字。

AbstractEventLoop.create_server()方法和start_server()函数创建的对象。不要直接实例化类。

close()

停止投放:关闭侦听套接字,并将sockets属性设置为None

表示现有传入客户端连接的套接字保持打开状态。

服务器异步关闭,使用wait_closed()协程等待,直到服务器关闭。

coroutine wait_closed()

等待直到close()方法完成。

此方法是coroutine

插座 T0> ¶ T1>

服务器正在侦听的socket.socket对象的列表,如果服务器关闭,则为None

18.5.1.17. 处理¶ T0>

class asyncio.Handle

一个回掉封装对象,由AbstractEventLoop.call_soon()AbstractEventLoop.call_soon_threadsafe()AbstractEventLoop.call_later()AbstractEventLoop.call_at()返回。

cancel()

取消呼叫。如果回调已经取消或执行,则此方法不起作用。

18.5.1.18. 事件循环的例子

18.5.1.18.1. Hello World with call_soon()

使用 AbstractEventLoop.call_soon() 方法来安排回调的示例。回调显示 "Hello World",然后停止事件循环︰

import asyncio

def hello_world(loop):
    print('Hello World')
    loop.stop()

loop = asyncio.get_event_loop()

# Schedule a call to hello_world()
loop.call_soon(hello_world, loop)

# Blocking call interrupted by loop.stop()
loop.run_forever()
loop.close()

也可以看看

Hello World coroutine示例使用coroutine

18.5.1.18.2. 用call_later()显示当前日期

回调示例每​​秒显示当前日期。回调使用AbstractEventLoop.call_later()方法在5秒内重新计划自身,然后停止事件循环:

import asyncio
import datetime

def display_date(end_time, loop):
    print(datetime.datetime.now())
    if (loop.time() + 1.0) < end_time:
        loop.call_later(1, display_date, end_time, loop)
    else:
        loop.stop()

loop = asyncio.get_event_loop()

# Schedule the first call to display_date()
end_time = loop.time() + 5.0
loop.call_soon(display_date, end_time, loop)

# Blocking call interrupted by loop.stop()
loop.run_forever()
loop.close()

也可以看看

显示当前日期的coroutine displaying the current date示例使用coroutine

18.5.1.18.3. 观察读取事件的文件描述符

等待文件描述器使用AbstractEventLoop.add_reader()方法接收到一些数据,然后关闭事件循环:

import asyncio
try:
    from socket import socketpair
except ImportError:
    from asyncio.windows_utils import socketpair

# Create a pair of connected file descriptors
rsock, wsock = socketpair()
loop = asyncio.get_event_loop()

def reader():
    data = rsock.recv(100)
    print("Received:", data.decode())
    # We are done: unregister the file descriptor
    loop.remove_reader(rsock)
    # Stop the event loop
    loop.stop()

# Register the file descriptor for read event
loop.add_reader(rsock, reader)

# Simulate the reception of data from the network
loop.call_soon(wsock.send, 'abc'.encode())

# Run the event loop
loop.run_forever()

# We are done, close sockets and the event loop
rsock.close()
wsock.close()
loop.close()

也可以看看

register an open socket to wait for data using a protocol示例使用由AbstractEventLoop.create_connection()方法创建的低级协议注册开放套接字以等待数据。

register an open socket to wait for data using streams示例使用协程中的open_connection()函数创建的高级流。

18.5.1.18.4. 为SIGINT和SIGTERM 设置信号处理程序

使用AbstractEventLoop.add_signal_handler()方法的信号SIGINTSIGTERM的寄存器处理程序:

import asyncio
import functools
import os
import signal

def ask_exit(signame):
    print("got signal %s: exit" % signame)
    loop.stop()

loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame),
                            functools.partial(ask_exit, signame))

print("Event loop running forever, press Ctrl+C to interrupt.")
print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid())
try:
    loop.run_forever()
finally:
    loop.close()

此示例仅适用于UNIX。