在同步方法上调用 await 时会发生什么?

What happens when await is called on synchronous methods?

我有同步上传和下载文件的方法(用sync_wait方法表示)。我想以一种永远同时并行执行上传器和下载器的方式异步和无休止地执行此操作。我是这样实现的:

运行 此代码:

import time, asyncio
from functools import wraps, partial

# 
def to_async(func):
    @wraps(func)
    async def run(*args, **kwargs):
        return await asyncio.get_event_loop().run_in_executor(None, partial(func, *args, **kwargs))
    return run

@to_async
def sync_wait(msg):
    time.sleep(msg)

async def producer(n, queue):
    while True:
        msg = .2
        await sync_wait(msg)
        print(f'{n}p')
        await queue.put(msg)

async def consumer(n, queue):
    while True:
        msg = await queue.get()
        print(f'{n}c')
        await sync_wait(msg)

async def main():
    queue = queue = asyncio.Queue(10)
    producers = [producer(n, queue) for n in range(2)]
    consumers = [consumer(n, queue) for n in range(4)]
    await asyncio.gather(*(producers + consumers), return_exceptions=True)

if __name__ == "__main__":
    asyncio.run(main())

打印了这个输出:

1p
0p
0c
1c
1p
2c
0p
3c
1p
2c
0p
1c
1p
0c
0p
3c
1p
2c
0p
3c
1p
0c
0p
3c
...

这是有道理的,因为我有 2 个生产者和 4 个消费者与我的队列进行交互。我的老板告诉我我不需要 to_async 装饰器。但是在从 sync_wait 定义中仅删除装饰器之后,我根本没有得到任何打印件。我该如何解释这种新行为?

当你 await 无法等待的事情时,事情会崩溃:

# python3 -m asyncio
asyncio REPL 3.9.9 (main, Jan 10 2022, 11:05:09) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> await time.sleep(.5)  # sleeps .5 secs before failing to await
Traceback (most recent call last):
...
TypeError: object NoneType can't be used in 'await' expression

但是,如果您将失败的东西放入 Taskgather 它们中,它们只会默默地失败,直到您真正等待它们检索它们的结果。

>>> async def fail():
...     print("about to fail...")
...     print(1/0)
...
>>> t = asyncio.create_task(fail())
about to fail...
>>> await t  # only fails noticeably when retrieving result
Traceback (most recent call last):
...
ZeroDivisionError: division by zero

因此,如果您将失败的任务放入具有无限 运行、non-failing 任务的 gather(..., return_exceptions=True),则永远不会报告失败。