python - Asyncio 如何使用 run_forever?
问题描述
我想做的事:
- 有一个启动的异步事件循环
- 该循环被传递给我系统中的各个类,用于调度协程
- 该循环还用于处理对事件的响应(即,我有一个队列,一些事件处理代码将在该队列上放置一个项目,并在该队列上等待 get() 以处理这些值的单独协程)
- 有一个主线程“拥有”循环并负责创建循环,并且在系统关闭时将取消循环上的任何正在运行的任务并关闭并停止循环(干净关闭)
我的理解是因为#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()
解决方案
有两种可能的方法:您可以在主线程或后台线程中运行事件循环。如果您在主线程中运行它,您需要run_forever
(run_until_complete(main())
或等效)作为程序初始化的最后一步。在这种情况下,主线程将“阻塞”,但这没关系,因为它的事件循环将是活动的并响应外部事件,从而允许程序运行。对调度协程和回调的事件循环的单个“阻塞”调用是 asyncio 的设计运行方式。
在不切实际的情况下,例如包含大量同步代码的程序,或者已经在多个线程之间进行通信的程序,通常最好创建一个专用线程并在其中运行事件循环。在这种情况下,您必须非常小心,不要与事件循环通信,而不是调用loop.call_soon_threadsafe()
and asyncio.run_coroutine_threadsafe()
。例如,__end_background_loop
必须使用调用,loop.call_soon_threadsafe(__end_background_loop)
因为它与任务和事件循环交互。这适用于与事件循环的所有交互 - 例如,loop.stop()
不允许从另一个线程调用,它必须拼写为loop.call_soon_threadsafe(loop.stop)
. 当然,从 asyncio 回调和协程调用循环函数很好,因为它们将始终在事件循环运行的同一线程中运行。
推荐阅读
- oracle - 将数据库连接导出到 Oracle Developer
- php - 未捕获的错误:调用未定义的函数 sqlsrv_connect()
- python - python数组表示法速记:“k%parents.shape[0]”是什么意思?
- laravel-5.7 - 生成带前缀 Laravel 的自动增量数
- java - Java 无效的赋值运算符
- autodesk-forge - 如何在 Forge Autodesk 中从浏览器缓存中保存/加载模型
- vue.js - 如何使用 Vue Test utils 测试输入是否聚焦?
- android - TextView 背景不换行内容
- javascript - JSDoc:显示模块的嵌套树而不是项目中所有模块的列表
- c# - 我想创建一个像 iTextSharp 这样的 PDF 编写器,而不是使用 iTextSharp - 从头开始创建 PDF