PySide6.QtAsyncio#
注意
此模块目前处于技术预览阶段。
QtAsyncio 模块是一个纯 Python 模块,允许编写结合 Qt API 和 [asyncio](https://docs.pythonlang.cn/3/library/asyncio.html) 使用的程序。asyncio 是一个流行的 Python 库,用于异步编程。它特别适用于需要处理许多来自多个源的 I/O 操作的程序,如 web 服务器。更一般地说,它允许开发者与 [coroutines](https://docs.pythonlang.cn/3/library/asyncio-task.html#coroutine) 工作在一起。可以将 coroutines 想象为“异步函数”。与 Qt 的信号和槽机制相比,这允许设计更接近于同步程序的异步程序,因为程序不再需要想象为一系列回调。相反,coroutines 在指定的位置透明地恢复和挂起。
考虑以下使用前面带有 async
关键字的简单协程定义:
async def do_something():
result = await do_something_asynchronously()
print(result)
do_something_asynchronously
是一个协程,例如,通常在同步程序中阻止单个执行流程的 I/O 重操作。相反,使用 await
关键字等待结果,此时 do_something
挂起并程序流程透明地切换到下一个异步任务。当结果可用时,程序流程能够切换回 do_something
协程,然后恢复并打印结果。
asyncio API#
asyncio 和 Qt 都基于事件循环。asyncio 提供了一个 API,用于用自定义实现替换它的默认事件循环。QtAsyncio 提供了一种实现,该实现使用 Qt 的事件循环,允许 Qt 和 asyncio 一起使用。
我们认为该 API 由两个层级组成:
事件循环和异步操作的基本基础设施,包括futures、tasks、handles、执行器以及事件循环管理函数(见下文)。
面向用户的API,用于应用程序,包括传输和协议、网络连接、服务器、套接字、信号、子进程。
QtAsyncio 当前涵盖了第一层。这包括以下功能,其API与asyncio中的API相同:
还包括在执行器中运行同步代码的能力(ThreadPoolExecutor)。
开始使用QtAsyncio#
要使用QtAsyncio编写程序,首先导入模块,例如:
import PySide6.QtAsyncio as QtAsyncio
QtAsyncio提供了一个函数run()
,该函数可以用来运行一个特定的协程直到其完成,或者直接启动Qt & asyncio事件循环。如果程序流程从该协程开始,则前者是有意义的;如果协程在程序流程的稍后部分被排入,例如在按下UI中的按钮后,则后者是有意义的。
QtAsyncio.run()
(参见asyncio "minimal" 示例对此用法的示例)或者
QtAsyncio.run(my_coroutine())
(参见asyncio "Eratosthenes" 示例)或者
QtAsyncio.run(my_coroutine(), keep_running=False)
运行协程并在其完成后停止事件循环。这种情况与asyncio.run(my_coroutine())
的行为相同。
可以传递一个额外的可选参数quit_qapp
到run()
,用来配置当asyncio完成时QtAsyncio的核心QCoreApplication是否应该关闭。需要禁用此功能的一个特殊情况是测试套件,它想要在整个单元测试中复用单个QCoreApplication实例,每次都关闭该实例将导致测试失败。默认值是True
。
请注意,这个参数与keep_running
参数是正交的。keep_running
确定在协程完成后asyncio是否应该继续运行,而quit_qapp
确定在asyncio完成后QCoreApplication是否应该关闭。asyncio可以在QCoreApplication保持活着的情况下完成。
一个参数handle_sigint
决定QtAsyncio是否应该处理SIGINT(Ctrl+C),并在收到后关闭事件循环。默认值是False
。如果您希望QtAsyncio处理SIGINT而不是您的程序,请设置此值为True
。
协程解释#
协程是一类可以在被暂停(yield)后再次恢复执行的函数。这个简单概念背后隐藏的是一个复杂的机制,该机制由异步框架抽象化。本次演讲将展示一张图表,试图说明协程从被提供给异步框架到完成的过程。