18.5.1. 基本事件循环¶
事件循环是 asyncio
提供的核心运行机制。它提供多个功能,包括︰
- class
asyncio.
BaseEventLoop
¶ 此类是实现细节。它是
AbstractEventLoop
的一个子类,也是在asyncio
中的具体事件循环实现的基类。不应直接使用; 而应该使用AbstractEventLoop
来代替。BaseEventLoop
不应该被第三方继承; 因为其类内部接口不是稳定的。
18.5.1.1. Run an event loop¶
-
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. Calls¶
大多数的 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)¶ 安排尽快调用的回调。当控制返回到事件循环,
call_soon()
返回后才进行回调。这些操作是类似 FIFO 队列,按照它们注册的顺序回调。每个回调将被调用一次。
任何位置参数将在回调执行时被传入。
实例返回的
asyncio.Handle
,可以用于取消回调。
-
AbstractEventLoop.
call_soon_threadsafe
(callback, *args)¶ 功能与
call_soon()
一致,但线程安全。请参阅文档的concurrency and multithreading部分。
18.5.1.3. Delayed calls¶
事件循环拥有属于自己的计时器来计算延时.使用哪种计时器取决于不同的操作系统提供的事件循环实现.这通常是与time.time()
不同的计时器。
注意
超时(相对延迟或绝对when)不应超过一天。
-
AbstractEventLoop.
call_later
(delay, callback, *args)¶ 安排在给定的延迟秒数(int或float)后调用回调函数。
返回
asyncio.Handle
的实例,可用于取消回调。call_later()
的每次执行只会调用回调函数一次。如果两个回调被调度完全相同的时间,它是未定义的,将首先调用。可选的位置args将在调用时传递给回调函数。如果希望执行某些已命名参数调用回调函数,请使用闭包或
functools.partial()
。
-
AbstractEventLoop.
call_at
(when, callback, *args)¶ Arrange for the callback to be called at the given absolute timestamp when (an int or float), using the same time reference as
AbstractEventLoop.time()
.此方法的行为与
call_later()
相同。返回
asyncio.Handle
的实例,可用于取消回调。
也可以看看
18.5.1.4. Futures¶
-
AbstractEventLoop.
create_future
()¶ 创建
asyncio.Future
对象附加到该循环。这是在asyncio中创建Futures的首选方式,因为事件循环实现可以提供Future类的替代实现(具有更好的性能或工具)。
版本3.5.2中的新功能。
18.5.1.5. Tasks¶
-
AbstractEventLoop.
create_task
(coro)¶ 第三方事件循环可以使用自己的
Task
子类来实现互操作性。在这种情况下,结果类型是Task
的子类。这个方法是在Python 3.4.2中添加的。使用
async()
函数也支持旧的Python版本。版本3.4.2中的新功能。
-
AbstractEventLoop.
set_task_factory
(factory)¶ 设置将由
AbstractEventLoop.create_task()
使用的任务工厂。如果factory为
None
,则将设置默认任务工厂。如果factory是可调用,则它应该具有与
匹配的声明(循环 coro) t2>,其中loop将是对活动事件循环的引用,coro将是协程对象。
可调用项必须返回asyncio.Future
兼容对象。版本3.4.4中的新功能。
-
AbstractEventLoop.
get_task_factory
()¶ 返回任务工厂,或
None
(如果使用默认工厂)。版本3.4.4中的新功能。
18.5.1.6. Creating connections¶
- 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_INET
或AF_INET6
> host(或family,如果指定),套接字类型SOCK_STREAM
。protocol_factory必须是可返回protocol的可调用实例。此方法是coroutine,它将尝试在后台建立连接。成功时,协程返回
(transport, 协议)
对。底层操作的时间概要如下:
- 建立连接,并创建transport以表示它。
- protocol_factory无参数调用,必须返回protocol实例。
- 协议实例绑定到传输,并且它的
connection_made()
方法被调用。 - 协程成功返回
(transport, 协议)
对。
创建的传输是依赖于实现的双向流。
注意
protocol_factory可以是任何类型的可调用,不一定是类。例如,如果要使用预创建的协议实例,可以传递
lambda: my_protocol
。更改连接创建方式的选项:
ssl:如果给定且不为false,则创建SSL / TLS传输(默认情况下创建纯TCP传输)。如果ssl是
ssl.SSLContext
对象,则此上下文用于创建传输;如果ssl为True
,则使用具有某些未指定的默认设置的上下文。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_host和local_port。
在版本3.5中已更改:在Windows上使用
ProactorEventLoop
,现在支持SSL / TLS。也可以看看
open_connection()
函数可用于获取一对(StreamReader
,StreamWriter
)而不是协议。
- 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
orAF_INET6
depending on host (or family if specified), socket typeSOCK_DGRAM
. protocol_factory必须是可返回protocol的可调用实例。此方法是coroutine,它将尝试在后台建立连接。成功时,协程返回
(transport, 协议)
对。更改连接创建方式的选项:
- local_addr(如果给出)是用于将套接字本地绑定的
(local_host, local_port)
使用getaddrinfo()
查找local_host和local_port。 - remote_addr(如果给出)是用于将套接字连接到远程地址的
(remote_host, remote_port) 。
使用getaddrinfo()
查找remote_host和remote_port。 - 家庭,proto,标志是可传递到
getaddrinfo()
用于主机分辨率。如果给定,这些都应该是相应的socket
模块常量的整数。 - reuse_address告诉内核重用TIME_WAIT状态中的本地套接字,而不必等待其自然超时到期。如果未指定,将在UNIX上自动设置为True。
- reuse_port告诉内核允许此端点绑定到与其他现有端点绑定的端口相同的端口,只要它们在创建时都设置此标志。此选项在Windows和某些UNIX上不受支持。如果未定义
SO_REUSEPORT
常量,则不支持此功能。 - allow_broadcast告诉内核允许此端点向广播地址发送消息。
- 可以可选地指定sock,以便使用先前存在的,已连接的
socket.socket
对象以供传输使用。如果指定,应省略local_addr和remote_addr(必须None
)。
在Windows上使用
ProactorEventLoop
,不支持此方法。- local_addr(如果给出)是用于将套接字本地绑定的
- coroutine
AbstractEventLoop.
create_unix_connection
(protocol_factory, path, *, ssl=None, sock=None, server_hostname=None)¶ 创建UNIX连接:套接字系列
AF_UNIX
,套接字类型SOCK_STREAM
。AF_UNIX
套接字系列用于高效地在同一机器上的进程之间进行通信。此方法是coroutine,它将尝试在后台建立连接。成功时,协程返回
(transport, 协议)
对。请参阅参数的
AbstractEventLoop.create_connection()
方法。可用性:UNIX。
18.5.1.7. Creating listening connections¶
- 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_INET
或AF_INET6
,以强制套接字使用IPv4或IPv6。如果未设置,则将从主机确定(默认为socket.AF_UNSPEC
)。 - flags是
getaddrinfo()
的位掩码。 - 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()
创建一个(StreamReader
,StreamWriter
)对,并使用此对调用函数。在版本3.5.1中更改: t> 主机参数现在可以是字符串序列。
- host参数可以是字符串,在这种情况下,TCP服务器绑定到主机和端口。host参数也可以是字符串序列,在这种情况下,TCP服务器绑定到序列的所有主机。如果host是空字符串或
- 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. Watch file descriptors¶
在Windows上使用SelectorEventLoop
,只支持套接字句柄(例如:不支持管道文件描述器)。
在Windows上使用ProactorEventLoop
,不支持这些方法。
-
AbstractEventLoop.
add_reader
(fd, callback, *args)¶ 开始观察文件描述器读取可用性,然后调用具有指定参数的回调。
-
AbstractEventLoop.
remove_reader
(fd)¶ 停止观看文件描述器读取可用性。
-
AbstractEventLoop.
add_writer
(fd, callback, *args)¶ 开始观察文件描述器的写可用性,然后调用具有指定参数的回调。
-
AbstractEventLoop.
remove_writer
(fd)¶ 停止观看文件描述器写入可用性。
watch a file descriptor for read events示例使用低级AbstractEventLoop.add_reader()
方法注册套接字的文件描述器。
18.5.1.9. Low-level socket operations¶
- 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. Resolve host name¶
- 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. Connect pipes¶
在Windows上使用SelectorEventLoop
,不支持这些方法。使用ProactorEventLoop
支持Windows上的管道。
- coroutine
AbstractEventLoop.
connect_read_pipe
(protocol_factory, pipe)¶ 在事件循环中注册读取管道。
protocol_factory应使用
Protocol
pipe是一个file-like object。返回对(传输, 协议)
,其中传输支持ReadTransport
接口。使用
SelectorEventLoop
事件循环,管道设置为非阻塞模式。此方法是coroutine。
- coroutine
AbstractEventLoop.
connect_write_pipe
(protocol_factory, pipe)¶ 在eventloop中注册写入管道。
protocol_factory应使用
BaseProtocol
接口实例化对象。pipe是file-like object。返回对(传输, 协议)
,其中传输支持WriteTransport
接口。使用
SelectorEventLoop
事件循环,管道设置为非阻塞模式。此方法是coroutine。
18.5.1.12. UNIX signals¶
可用性:仅UNIX。
-
AbstractEventLoop.
add_signal_handler
(signum, callback, *args)¶ 为信号添加处理程序。
如果信号编号无效或不可复位,则引发
ValueError
。如果设置处理程序时出现问题,则引发RuntimeError
。
-
AbstractEventLoop.
remove_signal_handler
(sig)¶ 删除信号的处理程序。
返回
True
如果删除了信号处理程序,False
如果没有。
也可以看看
signal
模块。
18.5.1.13. Executor¶
调用执行程序
(线程池或进程池)中的函数。默认情况下,事件循环使用线程池执行器(ThreadPoolExecutor
)。
- coroutine
AbstractEventLoop.
run_in_executor
(executor, func, *args)¶ 安排在指定的执行器中调用func。
executor参数应为
Executor
实例。如果executor为None
,则使用默认执行程序。Use functools.partial to pass keywords to the *func*。
此方法是 coroutine。
-
AbstractEventLoop.
set_default_executor
(executor)¶ 设置
run_in_executor()
使用的默认执行程序。
18.5.1.14. Error Handling API¶
允许定制在事件循环中如何处理异常。
-
AbstractEventLoop.
set_exception_handler
(handler)¶ 将处理程序设置为新的事件循环异常处理程序。
如果处理程序是
None
,则将设置默认异常处理程序。If handler is a callable object, it should have a matching signature to
(loop, context)
, whereloop
will be a reference to the active event loop,context
will be adict
object (seecall_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. Debug mode¶
-
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. Server¶
- class
asyncio.
Server
¶ 服务器侦听套接字。
由
AbstractEventLoop.create_server()
方法和start_server()
函数创建的对象。不要直接实例化类。-
close
()¶ 停止投放:关闭侦听套接字,并将
sockets
属性设置为None
。表示现有传入客户端连接的套接字保持打开状态。
服务器异步关闭,使用
wait_closed()
协程等待,直到服务器关闭。
-
sockets
¶ 服务器正在侦听的
socket.socket
对象的列表,如果服务器关闭,则为None
。
-
18.5.1.17. Handle¶
- 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()
也可以看看
18.5.1.18.2. Display the current date with 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()
也可以看看
The coroutine displaying the current date example uses a coroutine.
18.5.1.18.3. Watch a file descriptor for read events¶
等待文件描述器使用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. Set signal handlers for SIGINT and SIGTERM¶
使用AbstractEventLoop.add_signal_handler()
方法的信号SIGINT
和SIGTERM
的寄存器处理程序:
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。