Weyu‘s blog

  • 1. asyncio.sleep() 和 time.sleep() 有什么不同?
  • 2. 为什么 asyncio.sleep() 前面要加 await?
  • 3. 在 async 函数里,除了 sleep,还有哪些操作也需要 await?
  • 首页
  • 代码
  • 作品
  • 学习
  • 折腾
  • 随笔
  • 关于博主
  • 时光映像
  • 我的网盘
  • 文章归档
  • 友情链接

Python学习笔记:搞懂 asyncio从入门到不踩坑

  • admin
  • 2025-05-30
  • 0
import asyncio

async def f1():
    print('123')
    await asyncio.sleep(1)
    print('456')
    return("f1完成")

async def f2():
    print("abc")
    #time.sleep()不能在async协程函数中使用, 因为它是time库提供的一个 同步阻塞 函数, 调用它会暂停整个程序的执行流.
    #await -> 在 async 函数内部,要“等待”一个可等待对象完成,必须使用 await 关键字。
    # await 关键字的作用就是暂停当前协程的执行,并将控制权交给事件循环,直到被 await 的可等待对象完成。
    """
    await    它更广泛地应用于任何可能需要等待的、I/O 密集型的、异步操作,如:等待http请求返回响应时,
    例如,等待 HTTP 请求返回响应、读写文件、网络连接、数据库查询等等,
    这些操作的共同特点是:它们在执行过程中会有一段等待时间(等待数据从网络传输、等待硬盘读写等)。
    #!也就是说 , 在async协程函数中, 不光是sleep这样的操作, 例如 等待http请求返回响应比较耗时的操作, 也可以用await标注, 将这个可能耗时的操作 在等待的时候 交给async去先执行其他 循环事件
      """
    await asyncio.sleep(1) 
    print('def')
    return("f2完成")

async def main():
    #将 async协程对象封装为task对象
    task1 = asyncio.create_task(f1())
    task2 = asyncio.create_task(f2())

    #asyncio.wait([func1(),func2[]]) 返回 已完成的任务集合 和 未完成的任务集合
    #asyncio.wait() 是一个协程函数,它返回一个可等待对象。要让它实际执行并等待任务完成,必须在它前面加上 await 关键字。
    done, pending = await asyncio.wait([task1,task2])
    for task in done:
        print (f"完成的:{task.result()}")
    if pending:
        print(f"未完成的:{pending}")
    
if __name__ == '__main__':
#loop = asyncio.get_event_loop()    # 获取事件循环
#loop.run_until_comlete( result )   # 运行事件循环
# 在 python3.7 之后,可以省略上面两行,直接执行下面一行函数即可
    asyncio.run(main())


刚开始接触 Python 里的 asyncio 觉得有点儿绕。现学现现记,现在就记录下学习 asyncio 时经常会遇到的几个地方,把一些概念弄清楚。

1. asyncio.sleep() 和 time.sleep() 有什么不同?

当你需要在 asyncio 的程序里让代码暂停一会儿时,可能会想到用 sleep。但这里面有个小秘密:

  • time.sleep(1): 这是 Python 标准库里的一个函数。如果你在 async 函数里用它,比如 time.sleep(1),你的整个程序会完全停下来 1 秒。这就像按了程序的暂停键,其他所有的任务都得跟着一起等。在 asyncio 里,我们希望的是多个任务能轮流执行,而不是互相等待。所以,time.sleep() 会阻塞 asyncio 的“事件循环”,让并发失效。
  • asyncio.sleep(1): 这个是 asyncio 库专门提供的。当你写 await asyncio.sleep(1) 时,你告诉程序:“我这个任务要等 1 秒,你先去看看有没有其他任务可以跑,等 1 秒到了再回来叫我。” 这样,asyncio 就可以在等待的这 1 秒里去处理其他任务,实现并发。它不会阻塞整个程序。

简单来说: 在 async 函数里,需要暂停的话,一定要用 await asyncio.sleep()。


2. 为什么 asyncio.sleep() 前面要加 await?

你可能会问,既然用了 asyncio.sleep(),为什么前面还要加个 await 呢?

  • await 的作用: await 就像一个“等待”的信号。它告诉 Python,它后面跟着的操作(比如 asyncio.sleep(1) 或者一个网络请求)可能需要一些时间才能完成。当程序遇到 await 时,它会暂时停下当前任务,把控制权交给 asyncio 的“事件循环”。事件循环就能趁这个机会去执行其他已经准备好的任务。等到之前被 await 的操作完成了,事件循环再回来让这个任务继续执行。
  • 不加 await 会怎样? 如果你只写 asyncio.sleep(1) 而不加 await,程序会创建一个“任务对象”,但这个任务对象并不会被真正地“等待”和执行。它就像你建了一个房子图纸,但没开始盖。你可能会看到一个警告,告诉你这个协程“从没被等待过”。而且,程序也不会真的暂停。

所以: 在 async 函数里,任何需要等待异步操作的地方,比如 asyncio.sleep() 或者发送网络请求,前面都必须加上 await。


3. 在 async 函数里,除了 sleep,还有哪些操作也需要 await?

await 不仅仅是为了 sleep。它的真正厉害之处在于处理那些耗时但又不需要一直占用 CPU 的操作。

想象一下,你的程序需要:

  • 发送一个 HTTP 请求并等待响应: 比如从网站上下载数据。这会涉及到网络传输,需要等待服务器回应。
  • 读取或写入文件: 当程序在硬盘上读写文件时,也需要等待。
  • 查询数据库: 和数据库通信,等待查询结果。

这些操作都有一个共同点:它们在等待数据回来的时候,CPU 实际上是空闲的。如果用传统的方式,程序就会在那儿干等着。

在 asyncio 里,当你在这些操作前面加上 await 时(比如 await response.text() 或者 await aiofiles.open('file.txt').read()),你的程序就会:

  1. 暂停当前任务。
  2. 把控制权交给 asyncio 事件循环。
  3. 事件循环会去执行其他准备好的任务。
  4. 等到网络数据回来、文件读写完成、数据库返回结果时,事件循环会再回到这个任务,让它继续往下执行。

这样,你的程序就可以在等待一个网络请求的同时,去处理另一个网络请求,或者做其他计算,大大提高了效率。这就像你同时开了好几个订单,不需要一个一个地等,而是可以同时处理。

记住: 在 async 函数里,凡是那些需要等待外部资源(比如网络、硬盘、数据库)的操作,我们都会使用 await 来配合相应的异步库(比如 aiohttp 处理 HTTP 请求,aiofiles 处理文件)。

© 2025 Weyu‘s blog
  • {{ item.name }}
  • {{ item.name }}