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 由两个层级组成:

  1. 事件循环和异步操作的基本基础设施,包括futurestaskshandles、执行器以及事件循环管理函数(见下文)。

  2. 面向用户的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_qapprun(),用来配置当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)后再次恢复执行的函数。这个简单概念背后隐藏的是一个复杂的机制,该机制由异步框架抽象化。本次演讲将展示一张图表,试图说明协程从被提供给异步框架到完成的过程。

Asynchronous programming with asyncio and Qt