当前位置: 首页 > 知识库问答 >
问题:

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

尚嘉勋
2023-03-14

我已经读了很多例子,博客文章,问题/答案关于asyncio/async/wait在Python 3.5中,许多是复杂的,我发现最简单的可能是这个。
仍然使用ensure_future,为了学习Python中的异步编程,我想看一个更小的示例,以及执行基本异步/等待示例所需的最小工具是什么。

问题:是否可以给出一个简单的示例,说明async/await是如何工作的,只使用这两个关键字code运行异步循环—其他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())

但如果没有,则确保未来的,并仍然演示wait/async是如何工作的。


共有3个答案

扈瑞
2023-03-14

Python3.7现在有了一个更简单的API(在我看来)和一个更简单的措辞(比“确保未来”更容易记住):您可以使用create\u task,它返回一个任务对象(如果需要的话,以后可以使用它来取消任务)。

import asyncio

async def hello(i):
    print(f"hello {i} started")
    await asyncio.sleep(4)
    print(f"hello {i} done")

async def main():
    task1 = asyncio.create_task(hello(1))  # returns immediately, the task is created
    await asyncio.sleep(3)
    task2 = asyncio.create_task(hello(2))
    await task1
    await task2

asyncio.run(main())  # main loop

结果:

你好1开始了你好2开始了你好1结束了你好2结束了

如果需要获取这些异步函数的返回值,那么聚集是有用的。下面的示例是受留档的启发,但不幸的是,文档没有显示什么聚集是真正有用的:获取返回值!

import asyncio

async def factorial(n):
    f = 1
    for i in range(2, n + 1):
        print(f"Computing factorial({n}), currently i={i}...")
        await asyncio.sleep(1)
        f *= i
    return f

async def main():
    L = await asyncio.gather(factorial(2), factorial(3), factorial(4))
    print(L)  # [2, 6, 24]

asyncio.run(main())

预期产出:

计算阶乘(2),当前i=2…
计算阶乘(3),当前i=2…
计算阶乘(4),当前i=2…
计算阶乘(3),当前i=3…
计算阶乘(4),当前i=4…
[2,6,24]

PS:即使您使用的是asyncio,而不是trio,后者的教程也有助于我学习Python异步编程。

羿宏硕
2023-03-14

有没有可能给出一个简单的例子来展示async/wait是如何工作的,只使用这两个关键字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阻塞操作(如读取/写入网络)时获得性能优势。为了编写有用的示例,您需要使用这些操作的异步实现。

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

Upd:

好的,下面的示例使用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是如何开始的,仅仅一秒钟后,两个都完成了。

洪鸿
2023-03-14

为了回答您的问题,我将为同一问题提供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:异步/等待做错了

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/wait done right(与案例2相同,除了sleep功能)

import asyncio
import time

async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.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 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秒,而案例33秒。因此,async/await done right更快。

差异的原因在于sleep功能的实现。

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

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

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

sleepcase1case2中的功能是“相同的”。他们在不允许他人使用资源的情况下“睡觉”。而案例3允许在资源处于Hibernate状态时访问资源。

案例2中我们将异步添加到了正常函数中。但是,事件循环将不中断地运行它。为什么?因为我们没有告诉在哪里允许循环中断函数来运行另一个任务。

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

# case 3
async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1) # <-- Right here!

更多关于这一点,请阅读这里

更新02/五月/2020

考虑阅读

  • 异步编程搭便车指南
  • Asyncio期货和协同程序
 类似资料:
  • 我试图在react/electron项目中使用async/await,但它不起作用。我想要的是获取docker容器状态列表。但是安慰。日志(列表)返回未定义的。 有人能帮我吗?:)

  • 问题内容: 我想删除一些mongodb集合,但这是一个异步任务。代码将是: 控制台显示: 确保删除所有收藏集后将被打印的最简单方法是什么?任何第三方都可以用来简化代码。 问题答案: 我看到您正在使用,因此您正在谈论服务器端JavaScript。在这种情况下,我建议您查看异步模块并使用。您会发现此模块确实有用-它是为解决您所遇到的问题而开发的。您的代码可能如下所示

  • 我遇到了一些使用c#的/关键字进行异步编程的最佳实践(我是c# 5.0的新手)。 给出的建议之一如下: 稳定性:了解您的同步上下文 ...一些同步上下文是不可重入的和单线程的。这意味着在给定时间只能在上下文中执行一个工作单元。这方面的一个例子是Windows UI线程或ASP.NET请求上下文。在这些单线程同步上下文中,很容易死锁。如果您从单线程上下文中生成一个任务,然后在上下文中等待该任务,您的

  • 我正在尝试将数据库调用移出控制器,以清理并使其可测试。当它们在控制器中时,一切都会顺利进行。我将它们移出控制器,并添加了一个异步,以确保我们等待。否则,我将调用的中的函数。现在,一旦我使用async/await,控制器中的函数就会认为没有用户,因为它没有等待。 有几个关于异步等待的SO问题,但我没有找到一个解决我的问题。我确实验证了返回了我的用户,并添加了控制台日志来显示路径。 节点猫鼬异步等待似

  • 任务或任务 我们也可以定义自己的可实现对象。对象应具有以下资格。 < li >它有一个GetAwaiter()方法(实例方法或扩展方法); < li >其GetAwaiter()方法返回一个Awaiter。在下列情况下,对象是一个标识符: < ul > < li >它实现INotifyCompletion或ICriticalNotifyCompletion接口; < li >它有一个IsCompl

  • 我通读了Dart/flatter中的Async/Await/then,试图理解为什么aysnc函数中的Await不会等到完成后再继续。在我的UI中,有一个按钮调用一个异步方法来返回一个位置,该位置总是返回null,并且不等待函数完成。 该函数将调用推送到一个新的UI页面,该页面选择一个位置,并应返回一个结果。如何使该函数等待结果?我不是在使用异步吗?