我想做的事:
-
有一个asyncio事件循环,可以旋转
-
该循环被传递给我的系统中的各个类,用于调度协同程序
-
该循环也用于处理对事件的响应(即,我有一个队列,一些事件处理代码将在该队列上放置一个项目,以及单独的协同例程等待该队列上的get()来处理这些值)
-
有一个主线程"owns"循环并负责创建循环,并且在系统关闭时将取消循环上的任何正在运行的任务并关闭并停止循环(干净地关闭)
我的理解是因为#3,需要在循环中调用 run_forever()
以确保在循环上调度任务 . 但是如果我调用 run_forever()
然后我的主线程阻塞,永远不会终止 .
我尝试过的:
生成一个线程,传入循环,然后在线程中调用 run_forever
. 这意味着我的单元测试永远不会完成 . 要旨:
def __start_background_loop(loop):
def run_forever(loop):
loop.run_forever()
# because run_forever() will block the current thread, we spawn
# a subthread to issue that call in.
thread = Thread(target=run_forever, args=(loop,))
thread.start()
def __end_background_loop(loop):
for task in Task.all_tasks(loop):
task.cancel()
loop.stop()
1 回答
有两种可能的方法:您可以在主线程或后台线程中运行事件循环 . 如果在主线程中运行它,则需要
run_forever
(或run_until_complete(main())
或等效的)作为程序初始化的最后一步 . 在这种情况下,主线程将"block",但这没关系,因为它的事件循环将是实时的并响应外部事件,允许程序运行 . 对调度协同程序和回调的事件循环的单个"blocking"调用是asyncio的设计运行方式 .如果这是不切实际的,例如包含大量同步代码的程序,或者已经在多个线程之间进行通信的程序,那么创建专用线程并在其中运行事件循环通常是更好的主意 . 在这种情况下,除了调用loop.call_soon_threadsafe()和asyncio.run_coroutine_threadsafe()之外,您必须非常小心不要与事件循环通信 . 例如,必须使用
loop.call_soon_threadsafe(__end_background_loop)
调用__end_background_loop
,因为它与任务和事件循环交互 . 这适用于与事件循环的所有交互 - 例如,不允许从另一个线程调用loop.stop()
,它必须拼写为loop.call_soon_threadsafe(loop.stop)
. 当然,从asyncio回调和协同程序调用循环函数很好,因为它们总是在事件循环运行的同一个线程中运行 .