Python 中最简单的异步/等待示例

新手上路,请多包涵

我读过很多关于 asyncio / async / await 在 Python 3.5+ 中的问题/答案,我发现很多都是复杂的大概是 这个

它仍然使用 ensure_future ,为了学习 Python 中的异步编程,我希望看到一个更小的示例,以及执行基本异步/等待示例 所需的最少工具是 什么。

问题:是否可以举一个 简单的例子来说明 async / await 是如何工作 的,只使用这两个关键字 + 运行异步循环的代码 + 其他 Python 代码而不是其他 asyncio 函数?

示例:像这样:

 import asyncio

async def async_foo():
    print("async_foo started")
    await asyncio.sleep(5)
    print("async_foo done")

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()
    print('Do some actions 1')
    await asyncio.sleep(5)
    print('Do some actions 2')

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

但没有 ensure_future ,并且仍然演示 await / async 的工作原理。

原文由 Basj 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 664
2 个回答

为了回答您的问题,我将针对同一问题提供 3 种不同的解决方案。

案例 1:只是普通的 Python

 import time

def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()
tasks = [
    sum("A", [1, 2]),
    sum("B", [1, 2, 3]),
]
end = time.time()
print(f'Time: {end-start:.2f} sec')

输出:

 Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6

Time: 5.02 sec

案例 2:async/await 做错了

import asyncio
import time

async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

async def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        await sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()

loop = asyncio.get_event_loop()
tasks = [
    loop.create_task(sum("A", [1, 2])),
    loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

end = time.time()
print(f'Time: {end-start:.2f} sec')

输出:

 Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6

Time: 5.01 sec

案例 3:async/await 正确完成

与情况 2 相同,除了 sleep 函数:

 async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1)

输出:

 Task A: Computing 0+1
Time: 0.00
Task B: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task B: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 3+3
Time: 2.00
Task B: Sum = 6

Time: 3.01 sec

案例 1 和案例 2 给出相同的 5 秒,而案例 3 仅 3 秒。所以 async/await 正确完成 会更快。

差异的原因在于 sleep 函数的实现。

 # case 1
def sleep():
    ...
    time.sleep(1)

# case 2
async def sleep():
    ...
    time.sleep(1)

# case 3
async def sleep():
    ...
    await asyncio.sleep(1)

在情况 1 和情况 2 中,它们是“相同的”:它们在不允许其他人使用资源的情况下“休眠”。而在情况 3 中,它允许在睡眠时访问资源。

在案例 2 中,我们将 async 添加到正常函数中。然而,事件循环将 不间断 地运行它。为什么?因为我们没有说循环在哪里允许中断你的函数来运行另一个任务。

在案例 3 中,我们告诉事件循环在何处中断函数以运行另一个任务。具体在哪里?就在这儿!

 await asyncio.sleep(1)

更多信息请阅读 这里

2020 年 5 月 2 日更新

考虑阅读

原文由 Levon 发布,翻译遵循 CC BY-SA 4.0 许可协议

is it possible to give a simple example showing how async / await works, by using only these two keywords + asyncio.get_event_loop() + run_until_complete +其他 Python 代码但没有其他 asyncio 函数?

这样就可以编写有效的代码:

 import asyncio

async def main():
    print('done!')

if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

但是这样就无法证明为什么需要 asyncio。

顺便说一句,为什么你需要 asyncio ,而不仅仅是纯代码?答案是 - asyncio 允许您在并行化 I/O 阻塞操作(如读取/写入网络)时获得性能优势。要编写有用的示例,您需要使用这些操作的异步实现。

请阅读 此答案 以获得更详细的解释。

更新:

好的,下面是使用 asyncio.sleep 模拟 I/O 阻塞操作和 asyncio.gather 的示例,展示了如何同时运行多个阻塞操作:

 import asyncio

async def io_related(name):
    print(f'{name} started')
    await asyncio.sleep(1)
    print(f'{name} finished')

async def main():
    await asyncio.gather(
        io_related('first'),
        io_related('second'),
    )  # 1s + 1s = over 1s

if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

输出:

 first started
second started
first finished
second finished
[Finished in 1.2s]

请注意 io_related 是如何开始的,仅仅一秒钟后,两者都完成了。

原文由 Mikhail Gerasimov 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题