zoukankan      html  css  js  c++  java
  • python3.7中的async和await解析

    参考自: https://blog.csdn.net/songjinxaing/article/details/103883210
    感谢作者分享,此处记录以免丢失

    通过代码来解说原理
    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    /*
    预期想要的结果
    ----start foo
    (等待一秒)
    ----end foo
    */
    

    这个函数表示,先打印start foo 然后等待一秒, 然后再打印end foo
    这个函数不能直接被执行. 它必须放在一个异步环境中才能执行. 这个异步环境独立在整个程序之外,可以把所有的异步环境打包成一个箱子, 看成是一个同步事件.
    把这个函数装在这个异步环境里 异步环境的长度取决于环境里需要执行事件最长的那个函数
    开启这个异步环境的标志是 asyncio.run(foo())

    这条命令执行了之后,异步环境就被开启了. 需要主要的事, 同一线程同一时间只能开启一个异步环境. 换句话说, 在run函数里面的函数(本例中为bar())里面不能再包含run函数.

    因此, 上例需要执行的话:

    async def foo():
        print('start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    if __name__ == '__main__':
        asyncio.run(foo())
    
    

    例2

    def foo2():
        print('----start foo')
        time.sleep(1)
        print('----end foo')
    
    def bar2():
        print('----start bar')
        time.sleep(2)
        print('----end bar')
    
    if __name__ == '__main__':
        foo2()
        bar2()
    
    /*
    预期输出:
    ----start foo
    (等待1秒)
    ----end foo
    ----start bar
    (等待2秒)
    ----end bar
    */
    
    

    把上面的函数改写成异步之后

    我们想要的结果是
    
    ----start foo
    
    ****start bar
    
    (等待1秒)
    
    ----end foo
    
    (等待1秒)
    
    ****end bar
    
    但是运行上面的程序 结果却是
    
    ----start foo
    
    (等待1秒)
    
    ----end foo
    
    ****start bar
    
    (等待2秒)
    
    ****end bar
    
    这是为什么呢
    
    

    await表示 等待后面的异步函数操作完了之后, 执行下面的语句.

    所以在在本例中,await foo 等待foo函数完全结束了之后, 再去执行

    那么如何一起执行呢

    基本的有两种方法

    1.采用函数gather

    官方文档中的解释是

    awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

    并发 运行 aws 序列中的 可等待对象。

    如果 aws 中的某个可等待对象为协程,它将自动作为一个任务加入日程。

    如果所有可等待对象都成功完成,结果将是一个由所有返回值聚合而成的列表。结果值的顺序与 aws 中可等待对象的顺序一致。

    如果 return_exceptions 为 False (默认),所引发的首个异常会立即传播给等待 gather() 的任务。aws 序列中的其他可等待对象 不会被取消 并将继续运行。

    如果 return_exceptions 为 True,异常会和成功的结果一样处理,并聚合至结果列表。

    如果 gather() 被取消,所有被提交 (尚未完成) 的可等待对象也会 被取消。

    如果 aws 序列中的任一 Task 或 Future 对象 被取消,它将被当作引发了 CancelledError 一样处理 – 在此情况下 gather() 调用 不会 被取消。这是为了防止一个已提交的 Task/Future 被取消导致其他 Tasks/Future 也被取消。

    因此代码就有了

    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def bar():
        print('****start bar')
        await asyncio.sleep(2)
        print('****end bar')
    
    async def main():
        res = await asyncio.gather(foo(), bar())
        print(res)
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    

    返回值为函数的返回值列表 本例中为[None, None]
    第二种方法 创建task
    asyncio.create_task(coro)

    将 coro 协程 打包为一个 Task 排入日程准备执行。返回 Task 对象。

    该任务会在 get_running_loop() 返回的loop中执行,如果当前线程没有在运行的loop则会引发 RuntimeError。

    此函数 在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future() 函数。

    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def bar():
        print('****start bar')
        await asyncio.sleep(2)
        print('****end bar')
    
    async def main():
        asyncio.create_task(foo())
        asyncio.create_task(bar())
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    

    但是运行一下就会发现, 只输出了

    ----start foo
    ****start bar

    这是因为,create_task函数只是把任务打包放进了队列,至于它们有没有运行完. 不管.

    因此需要等待它们执行完毕.

    最后的代码为

    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def bar():
        print('****start bar')
        await asyncio.sleep(2)
        print('****end bar')
    
    async def main():
        task1 = asyncio.create_task(foo())
        task2 = asyncio.create_task(bar())
    
        await task1
        await task2
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    

    如果有多个请求

    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def main():
        tasks = []
        for i in range(10):
            tasks.append(asyncio.create_task(foo()))
        await asyncio.wait(tasks)
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    
    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def bar():
        print('****start bar')
        await asyncio.sleep(2)
        print('****end bar')
    
    async def main():
        tasks = []
        for i in range(10):
            tasks.append(asyncio.create_task(foo()))
        for j in range(10):
            tasks.append(asyncio.create_task(bar()))
        await asyncio.wait(tasks)
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    

    异步嵌套

    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def bar():
        print('****start bar')
        await asyncio.sleep(2)
        print('****end bar')
    
    async def foos():
        print('----------------------')
        tasks = []
        for i in range(3):
            tasks.append(asyncio.create_task(foo()))
        await asyncio.wait(tasks)
    
    async def main():
        tasks = []
        for i in range(3):
            tasks.append(asyncio.create_task(foos()))
        for j in range(3):
            tasks.append(asyncio.create_task(bar()))
        await asyncio.wait(tasks)
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    

    把每一个create_task当成新增了一条线. 这条线如果遇到IO操作了(即遇到了await) 那么就先等待在这里, 先执行别的线上的操作(如果已经有了结果)

    create了线才可以跳来跳去, 如果不create, 是不会跳走的

    async def foo():
        print('----start foo')
        await asyncio.sleep(1)
        print('----end foo')
    
    async def foos():
        print('----------------------')
        tasks = []
        await foo()
        await foo()
        await foo()
    
    async def main():
        tasks = []
        for i in range(3):
            tasks.append(asyncio.create_task(foos()))
        await asyncio.wait(tasks)
    
    if __name__ == '__main__':
        asyncio.run(main())
    
    

    这个例子里面 只创造了3条线, 因此只能3个3个执行, 其实应该9个一起等, 但是因为没有create_task所以并不会一起执行

  • 相关阅读:
    CSS绿色导航代码
    一款简单另类的CSS导航菜单代码
    来自腾讯QQ网站首页的选项卡菜单代码
    JavaScript学习笔记之应用技巧二
    JavaScript学习笔记之创建字符串比较
    Delegate学习笔记
    ADO.NET Entity FrameWork学习笔记
    Reflector学习之特性理解
    字符串范围截取(转载)
    Delegate学习笔记之事件订阅
  • 原文地址:https://www.cnblogs.com/zunwen/p/14027338.html
Copyright © 2011-2022 走看看