检测空闲异步事件循环
Detect an idle asyncio event loop
是否有某种编程模式可以让我检测到 asyncio 事件循环在以下意义上何时变为空闲?假设我的执行路径以某种复杂的方式分支,比如使用 asyncio.gather(),但我知道每个分支最终都会等待一些空闲协程,例如套接字或子进程。假设我知道这些协同程序实际上永远不会让步,所以事件循环将执行它可以执行的任何 python 代码,但最终只会等待那些空闲的协同程序。有没有一种程序化的方法来检测这种状态并停止循环?
正如 Philip Couling 所指出的,下面提出的解决方案不起作用。 Whosebug 不允许删除已接受的答案,因此我添加了此免责声明。
您所说的“空闲”可能更准确地描述为“等待 IO 或超时”。在正确编写的 asyncio 代码中,不需要检测循环是否处于该状态,因为它不应该 重要 - 循环正在完成它的工作,这取决于工具像 asyncio.gather
、asyncio.wait
和 loop.run_until_complete
以确保它在适当的时间结束。然而,事情并不总是完美的,如果你真的想那样做,那当然是可以的。
在事件循环的每一步,它都会检查准备好 运行 的任务。如果有,则调用它们的步骤。一旦没有更多任务准备就绪,事件循环将等待 IO 事件或最快的超时,以先发生者为准。需要注意的重要一点是 运行ning 任务总是优先于等待 IO。因此,要检测没有任务就绪的情况,可以安排一个已知会立即触发的虚拟 IO 事件。
以下协程设置这样一个事件并等待它触发:
import socket, asyncio
async def detect_iowait():
loop = asyncio.get_event_loop()
rsock, wsock = socket.socketpair()
wsock.close()
await loop.sock_recv(rsock, 1)
rsock.close()
它设置了一个 socket pair 从一个套接字读取 returns 写入另一个套接字的数据。它立即关闭其中一个套接字,以便立即从另一个套接字读取 returns EOF,表示为空字节数组。等待从该套接字读取基本上是非阻塞的 - 但 asyncio
不知道这一点,因此它将套接字放在 IO 等待列表中。如上所述,一旦不存在 运行 可用任务,asyncio 将等待 IO,并且 detect_iowait
将等待套接字上的读取并退出。因此等待 detect_iowait()
本身检测到 IO 等待。
使用 detect_iowait()
的测试代码可能如下所示:
# stop loop.run_forever once iowait is detected
async def stop_on_iowait():
await detect_iowait()
print('iowait detected, stopping!')
asyncio.get_event_loop().stop()
# a dummy calculation coroutine, emulating your execution path
async def calc(n):
print('calc %d start' % n)
async def noop():
pass
for i in range(n):
await noop()
print('calc %d end' % n)
# coroutine that waits on IO forever, also (ab)using a socket pair,
# this time creating a socket whose recv will never complete
async def io_forever():
loop = asyncio.get_event_loop()
sock, _ = socket.socketpair()
sock.setblocking(False)
await loop.sock_recv(sock, 1)
loop = asyncio.get_event_loop()
for t in calc(1000), calc(10000), calc(100000), io_forever():
loop.create_task(t)
loop.create_task(stop_on_iowait())
loop.run_forever()
是否有某种编程模式可以让我检测到 asyncio 事件循环在以下意义上何时变为空闲?假设我的执行路径以某种复杂的方式分支,比如使用 asyncio.gather(),但我知道每个分支最终都会等待一些空闲协程,例如套接字或子进程。假设我知道这些协同程序实际上永远不会让步,所以事件循环将执行它可以执行的任何 python 代码,但最终只会等待那些空闲的协同程序。有没有一种程序化的方法来检测这种状态并停止循环?
正如 Philip Couling 所指出的,下面提出的解决方案不起作用。 Whosebug 不允许删除已接受的答案,因此我添加了此免责声明。
您所说的“空闲”可能更准确地描述为“等待 IO 或超时”。在正确编写的 asyncio 代码中,不需要检测循环是否处于该状态,因为它不应该 重要 - 循环正在完成它的工作,这取决于工具像 asyncio.gather
、asyncio.wait
和 loop.run_until_complete
以确保它在适当的时间结束。然而,事情并不总是完美的,如果你真的想那样做,那当然是可以的。
在事件循环的每一步,它都会检查准备好 运行 的任务。如果有,则调用它们的步骤。一旦没有更多任务准备就绪,事件循环将等待 IO 事件或最快的超时,以先发生者为准。需要注意的重要一点是 运行ning 任务总是优先于等待 IO。因此,要检测没有任务就绪的情况,可以安排一个已知会立即触发的虚拟 IO 事件。
以下协程设置这样一个事件并等待它触发:
import socket, asyncio
async def detect_iowait():
loop = asyncio.get_event_loop()
rsock, wsock = socket.socketpair()
wsock.close()
await loop.sock_recv(rsock, 1)
rsock.close()
它设置了一个 socket pair 从一个套接字读取 returns 写入另一个套接字的数据。它立即关闭其中一个套接字,以便立即从另一个套接字读取 returns EOF,表示为空字节数组。等待从该套接字读取基本上是非阻塞的 - 但 asyncio
不知道这一点,因此它将套接字放在 IO 等待列表中。如上所述,一旦不存在 运行 可用任务,asyncio 将等待 IO,并且 detect_iowait
将等待套接字上的读取并退出。因此等待 detect_iowait()
本身检测到 IO 等待。
使用 detect_iowait()
的测试代码可能如下所示:
# stop loop.run_forever once iowait is detected
async def stop_on_iowait():
await detect_iowait()
print('iowait detected, stopping!')
asyncio.get_event_loop().stop()
# a dummy calculation coroutine, emulating your execution path
async def calc(n):
print('calc %d start' % n)
async def noop():
pass
for i in range(n):
await noop()
print('calc %d end' % n)
# coroutine that waits on IO forever, also (ab)using a socket pair,
# this time creating a socket whose recv will never complete
async def io_forever():
loop = asyncio.get_event_loop()
sock, _ = socket.socketpair()
sock.setblocking(False)
await loop.sock_recv(sock, 1)
loop = asyncio.get_event_loop()
for t in calc(1000), calc(10000), calc(100000), io_forever():
loop.create_task(t)
loop.create_task(stop_on_iowait())
loop.run_forever()