如何快速启动Python异步协程?

问题描述 投票:0回答:3

考虑以下代码:

import asyncio

async def subsubfunc():
    print("    subsubfunc")
    # [some async I/O code here]

async def subfunc():
    print("  subfunc A")
    awaitable = subsubfunc()
    print("  subfunc B")
    await awaitable
    print("  subfunc C")

async def func():
    print("func A")
    awaitable = subfunc()
    print("func B")
    await awaitable
    print("func C")

asyncio.run(func())

实际产量:

func A
func B
  subfunc A
  subfunc B
    subsubfunc
  subfunc C
func C

所需输出:

func A
  subfunc A
    subsubfunc
  subfunc B
  subfunc C
func B
func C

更准确地说,我希望所有无法急切完成的工作都能够真正急切完成(在调用者继续执行之前)。

以下代码片段以一种相当黑客的方式模拟了这一点:

import asyncio

async def subsubfunc():
    print("    subsubfunc")
    # [some async I/O code here]

async def subfunc():
    print("  subfunc A")
    awaitable = asyncio.create_task(subsubfunc())
    await asyncio.sleep(0)
    print("  subfunc B")
    await awaitable
    print("  subfunc C")

async def func():
    print("func A")
    awaitable = asyncio.create_task(subfunc())
    await asyncio.sleep(0) # subfunc is started here
    await asyncio.sleep(0) # subsubfunc is started here
    print("func B")
    await awaitable
    print("func C")

asyncio.run(func())

有没有更干净的方法来做到这一点?

编辑:澄清一下:我希望

func B
subfunc B
在协程完成之前打印。想象一下 [some async I/O code here]
 等待 I/O。调用者应在 I/O 完成之前继续。一个可能的用例是,如果数据可以(并且必须)以非阻塞方式发送,然后同一个协程需要等待响应,并且在调用者之间应该执行额外的工作。

python python-asyncio coroutine
3个回答
2
投票
OP询问如何急切地运行协程,到目前为止所有的回复都是“协程不急切地执行”,这不是答案。

记录的答案是使用

asyncio.create_task(coroutine)

,它将协程变成任务,并安排其执行。然后任务就可以等待了,就像协程一样。

有关 python 文档的更多信息:

https://docs.python.org/3/library/asyncio-task.html#creating-tasks


0
投票
当你说,例如...

awaitable = subsubfunc()
...然后你说...

await awaitable
...这两个语句相当于单个语句:

await subsubfunc()
请注意,语句 

awaitable = subsubfunc()

 不会导致任何内容运行。只有当您
await
异步函数调用(或对此类函数调用的引用,例如变量
awaitable
)时,某些内容才会运行。

所以你只需要移动几个

await

语句:

import asyncio async def subsubfunc(): print(" subsubfunc") # [some async I/O code here] async def subfunc(): print(" subfunc A") awaitable = subsubfunc() await awaitable print(" subfunc B") print(" subfunc C") async def func(): print("func A") awaitable = subfunc() await awaitable print("func B") print("func C") asyncio.run(func())
可以简化为:

import asyncio async def subsubfunc(): print(" subsubfunc") # [some async I/O code here] async def subfunc(): print(" subfunc A") await subsubfunc() print(" subfunc B") print(" subfunc C") async def func(): print("func A") await subfunc() print("func B") print("func C") asyncio.run(func())
打印:

func A subfunc A subsubfunc subfunc B subfunc C func B func C
    

0
投票
在 Python 3.12 中,您无需通过

asyncio.sleep

 调用 hacky
 
asyncio.eager_task_factory
即可实现此目的

使用此工厂时,协程在任务构造期间开始同步执行。仅当任务阻塞时,才会在事件循环上安排任务。

import asyncio async def subsubfunc(): print(" subsubfunc") # [some async I/O code here] async def subfunc(): print(" subfunc A") awaitable = asyncio.create_task(subsubfunc()) print(" subfunc B") await awaitable print(" subfunc C") async def func(): print("func A") awaitable = asyncio.create_task(subfunc()) print("func B") await awaitable print("func C") loop = asyncio.new_event_loop() loop.set_task_factory(asyncio.eager_task_factory) loop.run_until_complete(func())


顺便说一下,使用

asyncio.sleep(0)

 是允许其他任务运行的有效方法,并记录在 
asyncio
 文档中
:

sleep() 总是挂起当前任务,允许其他任务运行。

将延迟设置为 0 可以提供优化的路径以允许其他任务运行。长时间运行的函数可以使用它来避免在函数调用的整个持续时间内阻塞事件循环。

© www.soinside.com 2019 - 2024. All rights reserved.