首页 文章

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

提问于
浏览
9

我在Python 3.5中阅读了很多关于 asyncio / async / await 的例子,博客文章,问题/答案,很多很复杂,我发现最简单的可能是this one . 它仍然使用 ensure_future ,并且为了学习Python中的异步编程,我想看看是否有更简单的例子是可能的(即,执行基本异步/等待示例所需的最小工具是什么) .

问题:为了学习Python中的异步编程,是否可以通过仅使用这两个关键字 asyncio.get_event_loop() run_until_complete 其他Python代码而不是其他 asyncio 函数来给出 simple example showing how async / await works

示例:类似这样的事情:

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的工作原理 .

2 回答

  • 8

    是否可以通过仅使用这两个关键字asyncio.get_event_loop()run_until_complete其他Python代码但没有其他asyncio函数,给出一个简单示例,说明async / await是如何工作的?

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

    import asyncio
    
    
    async def main():
        print('done!')
    
    
    if __name__ ==  '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    

    But this way it's impossible to demonstrate why you need asyncio.

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

    有关更详细的说明,请阅读this answer .

    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 如何开始,仅在一秒钟之后,两者都完成了 .

  • 3

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

    案例1: just normal 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 done wrong

    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 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
    

    case 1case 2 给出相同的 5 seconds ,而 case 3 只是 3 seconds . 所以 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)
    

    case 1case 2 中的 sleep 函数是"same" . 他们"sleep"而不允许其他人使用这些资源 . 而 case 3 允许在睡眠时访问资源 .

    case 2 中,我们将 async 添加到正常函数中 . 但是事件循环将运行它 without interruption . 为什么?因为我们没有告诉允许循环在哪里中断你的函数来运行另一个任务 .

    case 3 中,我们告诉事件循环确切地中断了运行另一个任务的函数的位置 . 到底在哪里?

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

    更多关于此阅读here

相关问题