轻松理解 Python 中的 async await 概念

轻松理解 Python 中的 async await 概念前言写这篇文章是受xinghun85的这篇博客的启发,但是人家后面写的东西跳跃太快,有点没看懂,自己在此做一个补充.我希望能用一个最平易近人的例子,把Python协程中的async/await概念讲清楚,希望能够帮助大家有一个形象化的认识.注:所有的讲解都在代码的注释里.fromtimeimportsleep,timedefdemo1():…

大家好,又见面了,我是你们的朋友全栈君。

前言

写这篇文章是受 xinghun85 的这篇博客 的启发, 但是人家后面写的东西跳跃太快, 有点没看懂, 自己在此做一个补充.

我希望能用一个最平易近人的例子, 把 Python 协程中的 async/await 概念讲清楚, 希望能够帮助大家有一个形象化的认识.

注: 所有的讲解都在代码的注释里.

from time import sleep, time


def demo1():
    """ 假设我们有三台洗衣机, 现在有三批衣服需要分别放到这三台洗衣机里面洗. """
    
    def washing1():
        sleep(3)  # 第一台洗衣机, 需要洗3秒才能洗完 (只是打个比方)
        print('washer1 finished')  # 洗完的时候, 洗衣机会响一下, 告诉我们洗完了
    
    def washing2():
        sleep(2)
        print('washer2 finished')
    
    def washing3():
        sleep(5)
        print('washer3 finished')
    
    washing1()
    washing2()
    washing3()
    
    """ 这个还是很容易理解的, 运行 demo1(), 那么需要10秒钟才能把全部衣服洗完. 没错, 大部分时间都花在挨个地等洗衣机上了. """


def demo2():
    """ 现在我们想要避免无谓的等待, 为了提高效率, 我们将使用 async. washing1/2/3() 本是 "普通函数", 现在我们用 async 把它们升级为 "异步函数". 注: 一个异步的函数, 有个更标准的称呼, 我们叫它 "协程" (coroutine). """
    
    async def washing1():
        sleep(3)
        print('washer1 finished')
    
    async def washing2():
        sleep(2)
        print('washer2 finished')
    
    async def washing3():
        sleep(5)
        print('washer3 finished')
    
    washing1()
    washing2()
    washing3()
    
    """ 从正常人的理解来看, 我们现在有了异步函数, 但是却忘了定义应该什么时候 "离开" 一台洗衣 机, 去看看另一个... 这就会导致, 现在的情况是我们一边看着第一台洗衣机, 一边着急地想着 "是不是该去开第二台洗衣机了呢?" 但又不敢去 (只是打个比方), 最终还是花了10秒的时间才 把衣服洗完. PS: 其实 demo2() 是无法运行的, Python 会直接警告你: RuntimeWarning: coroutine 'demo2.<locals>.washing1' was never awaited RuntimeWarning: coroutine 'demo2.<locals>.washing2' was never awaited RuntimeWarning: coroutine 'demo2.<locals>.washing3' was never awaited """


def demo3():
    """ 现在我们吸取了上次的教训, 告诉自己洗衣服的过程是 "可等待的" (awaitable), 在它开始洗衣服 的时候, 我们可以去弄别的机器. """
    
    async def washing1():
        await sleep(3)  # 注意这里加入了 await
        print('washer1 finished')
    
    async def washing2():
        await sleep(2)
        print('washer2 finished')
    
    async def washing3():
        await sleep(5)
        print('washer3 finished')
    
    washing1()
    washing2()
    washing3()
    
    """ 尝试运行一下, 我们会发现还是会报错 (报错内容和 demo2 一样). 这里我说一下原因, 以及在 demo4 中会给出一个最终答案: 1. 第一个问题是, await 后面必须跟一个 awaitable 类型或者具有 __await__ 属性的 对象. 这个 awaitable, 并不是我们认为 sleep() 是 awaitable 就可以 await 了, 常见的 awaitable 对象应该是: await asyncio.sleep(3) # asyncio 库的 sleep() 机制与 time.sleep() 不 # 同, 前者是 "假性睡眠", 后者是会导致线程阻塞的 "真性睡眠" await an_async_function() # 一个异步的函数, 也是可等待的对象 以下是不可等待的: await time.sleep(3) x = await 'hello' # <class 'str'> doesn't define '__await__' x = await 3 + 2 # <class 'int'> dosen't define '__await__' x = await None # ... x = await a_sync_function() # 普通的函数, 是不可等待的 2. 第二个问题是, 如果我们要执行异步函数, 不能用这样的调用方法: washing1() washing2() washing3() 而应该用 asyncio 库中的事件循环机制来启动 (具体见 demo4 讲解). """


def demo4():
    """ 这是最终我们想要的实现. """
    import asyncio  # 引入 asyncio 库
    
    async def washing1():
        await asyncio.sleep(3)  # 使用 asyncio.sleep(), 它返回的是一个可等待的对象
        print('washer1 finished')
    
    async def washing2():
        await asyncio.sleep(2)
        print('washer2 finished')
    
    async def washing3():
        await asyncio.sleep(5)
        print('washer3 finished')
    
    """ 事件循环机制分为以下几步骤: 1. 创建一个事件循环 2. 将异步函数加入事件队列 3. 执行事件队列, 直到最晚的一个事件被处理完毕后结束 4. 最后建议用 close() 方法关闭事件循环, 以彻底清理 loop 对象防止误用 """
    # 1. 创建一个事件循环
    loop = asyncio.get_event_loop()
    
    # 2. 将异步函数加入事件队列
    tasks = [
        washing1(),
        washing2(),
        washing3(),
    ]
    
    # 3. 执行事件队列, 直到最晚的一个事件被处理完毕后结束
    loop.run_until_complete(asyncio.wait(tasks))
    """ PS: 如果不满意想要 "多洗几遍", 可以多写几句: loop.run_until_complete(asyncio.wait(tasks)) loop.run_until_complete(asyncio.wait(tasks)) loop.run_until_complete(asyncio.wait(tasks)) ... """
    
    # 4. 如果不再使用 loop, 建议养成良好关闭的习惯
    # (有点类似于文件读写结束时的 close() 操作)
    loop.close()
    
    """ 最终的打印效果: washer2 finished washer1 finished washer3 finished elapsed time = 5.126561641693115 (毕竟切换线程也要有点耗时的) 说句题外话, 我看有的博主的加入事件队列是这样写的: tasks = [ loop.create_task(washing1()), loop.create_task(washing2()), loop.create_task(washing3()), ] 运行的效果是一样的, 暂不清楚为什么他们这样做. """


if __name__ == '__main__':
    # 为验证是否真的缩短了时间, 我们计个时
    start = time()
    
    # demo1() # 需花费10秒
    # demo2() # 会报错: RuntimeWarning: coroutine ... was never awaited
    # demo3() # 会报错: RuntimeWarning: coroutine ... was never awaited
    demo4()  # 需花费5秒多一点点
    
    end = time()
    print('elapsed time = ' + str(end - start))

参考

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/157975.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Unity | Cinemachine ClearShot Camera[通俗易懂]

    Unity | Cinemachine ClearShot Camera[通俗易懂]ClearShotCamera可以管理一组子虚拟相机,这些虚拟相机需要具有CinemachineCollider组件,ClearShotCamera可以实现角色被障碍物挡住时,虚拟摄像机的自动切换效果,如下所示,角色与Cam2被BoxCollider挡住时,虚拟相机由Cam2自动切换到Cam3。ClearShotCamera上有一个CinemachineClearShot组件,VirtualCameraChildren管理虚拟相机。CinemachineCollider既可以挂在所

    2022年5月28日
    37
  • isnotempty和isnotnull_isannotationpresent()用法

    isnotempty和isnotnull_isannotationpresent()用法引入包:org.apache.commons.lang3.StringUtils;1.publicstaticbooleanisEmpty(Stringstr)判断某字符串是否为空,为空的标准是str==null或str.length()==0下面是StringUtils判断是否为空的示例:StringUtils.isEmpty(null)=trueStringUtils.isEm…

    2022年9月27日
    5
  • JAVA中字符串和数组做参数传递的情况

    JAVA中字符串和数组做参数传递的情况首先明确的一点就是在java中只有值传递!只有值传递!理论依据来自《thinkinjava》。接下来就是具体说明为何java只有值传递。因为java中有基本类型和引用类型两种数据类型,再加上String这个特殊的类型,所以主要从三个方面就行解释。1.基本数据类型先看代码publicclassDemo01{publicvoidchange(inta){System.out.println(“副本a的初始…

    2022年5月6日
    86
  • matlab输出语句fprintf例子_matlab中compose函数

    matlab输出语句fprintf例子_matlab中compose函数golang需要什么基础?_后端开发golang需要的基础是:首先初学Go语言要弄懂基础语法和概念;然后掌握文件操作、网络编程、锁、协程、对象序列化和反序列化,以及各种数据格式的封装等;最后接触数据库等,就可以模块化开发。matlab中fprintf函数的用法详解:fprintf函数可以将数据按指定格式写入到文本文件中。其调用格式为:数据的格式化输出:fprintf(fid,format,v…

    2022年8月31日
    3
  • 2002-2007年最新免费空间

    2002-2007年最新免费空间赛腾网2006/11/06免费自助建站,150M存储空间,需要下载一个将近167M的软件来使用,但功能确实很强大,无广告。此免费空间的『演示』【推广】Fizwig2007/09/215G免费空间,每月10G流量,3个MySql数据库,ftp、web方式上传管理文件,有流量统计功能,可绑定域名(此服务未经测试),支持php,有广告。此免费空间的『演示』

    2022年7月27日
    24
  • web图书销售管理系统_解读图书管理系统为书店带来的好处

    web图书销售管理系统_解读图书管理系统为书店带来的好处图书管理系统的出现,极大地推动了大中小型书店的发展,使书店管理工作更加高效成为书店管理的重要软件。其中图书管理系统给行业的好处更是大大方便了行业的运作。一、图书管理系统在书店中的应用,主要是为了进销存的目的,为了提高书店的效率加速发展,但没有选择合适的图书管理系统往往事与愿违。图书管理制度的选择是书店经营管理工作中的重要环节。图书管理系统,尤其是进销存功能,是书店在经营过程中对采购、销售、财务等进…

    2022年6月8日
    45

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号