1 前言

最近在写爬虫(尤其是多分片ts视频下载)、高并发请求相关的代码时,发现在接触 Python 的async/await时一头雾水:

  • 为什么 async 定义的函数直接调用不执行?
  • await 到底是干嘛的?
  • 异步代码的执行顺序和同步到底有啥区别?
  • 遇到 await 程序到底是挂起还是等待?

本篇就用大白话+通俗例子+代码实例,从零讲懂 Python 异步编程的核心:async / await,不讲晦涩术语,新手也能轻松吃透,看完直接上手写异步代码!

2 先搞懂:同步 VS 异步,到底差在哪?

在学 async/await 之前,必须先分清同步异步的执行逻辑,这是理解异步的基础。

2.1 同步执行(阻塞式)

同步就是按顺序做事,一件做完再做下一件,遇到等待(比如网络请求、sleep、文件读写)时,程序会卡死等待,啥也不干,直到等待结束。

举个生活例子:烧水时站在水壶旁边死等,水开了再去洗菜,中间啥也不做。

代码示例

import time

# 同步任务1
def task1():
    print("任务1开始执行")
    time.sleep(2)  # 模拟等待2秒(阻塞)
    print("任务1执行完毕")

# 同步任务2
def task2():
    print("任务2开始执行")
    time.sleep(2)  # 模拟等待2秒(阻塞)
    print("任务2执行完毕")

# 同步执行
if __name__ == '__main__':
    start = time.time()
    task1()
    task2()
    end = time.time()
    print(f"同步执行总耗时:{end - start:.2f}秒")

执行结果

任务1开始执行
任务1执行完毕
任务2开始执行
任务2执行完毕
同步执行总耗时:4.01秒

结论:同步是串行执行,总耗时是所有任务等待时间之和,效率极低。

2.2 异步执行(非阻塞式)

异步就是不用死等,遇到等待就切换去做别的事,等待结束后再回来继续执行,同一时间可以处理多个任务,CPU 不会空转。

举个生活例子:点开烧水开关后,不用站着等,直接去洗菜,水开了再回来处理,两件事同时推进。

代码示例(异步基础版):

import asyncio
import time

# 异步任务1(async修饰,变成协程函数)
async def task1():
    print("任务1开始执行")
    await asyncio.sleep(2)  # 异步等待,非阻塞
    print("任务1执行完毕")

# 异步任务2
async def task2():
    print("任务2开始执行")
    await asyncio.sleep(2)  # 异步等待,非阻塞
    print("任务2执行完毕")

# 异步主函数
async def main():
    # 并发执行两个异步任务
    await asyncio.gather(task1(), task2())

if __name__ == '__main__':
    start = time.time()
    asyncio.run(main())  # 启动异步事件循环
    end = time.time()
    print(f"异步执行总耗时:{end - start:.2f}秒")

等待函数使用 await asyncio.sleep(2) 而不是 time.sleep(2)
执行结果

任务1开始执行
任务2开始执行
任务1执行完毕
任务2执行完毕
异步执行总耗时:2.01秒

结论:异步是并发执行,总耗时≈最长单个任务时间,效率大幅提升!

3 核心关键字 1:async,到底有什么用?

3.1 async 的作用

async 是用来定义协程函数的关键字,简单来说:

被 async 修饰的函数,不再是普通函数,直接调用不会执行函数体,只会返回一个协程对象(Coroutine Object),必须通过 await/事件循环才能真正执行。

3.2 关键误区:async 函数直接调用不执行!

很多新手踩坑:async 函数直接调用,发现里面的 print、逻辑都不跑,只返回一个奇怪的对象,这是正常现象!

错误示例

import asyncio

# 定义async协程函数
async def test():
    print("我是async函数")
    return 666

# 直接调用,不执行!
res = test()
print(res) #<coroutine object test at 0x000001234567890>

正确执行方式:必须放在异步函数里,用 await 调用,或通过 asyncio.run() 启动。

3.3 async 函数总结

  1. async 修饰 → 协程函数,非普通函数
  2. 直接调用 → 返回协程对象,不执行代码
  3. 必须配合 await/事件循环,才能真正运行

4 核心关键字 2:await,彻底讲明白

4.1 await 的通俗理解

你之前理解的完全正确!用一句话总结:

await = 挂起当前任务 + 切换执行其他任务 + 等待结束后回来继续

遇到 await 时,程序不会死等,而是把当前任务挂起,先去执行其他可运行的代码,等 await 的操作完成后,再回到原来的位置继续往下走。

4.2 await 的两大铁律

  • 律 1:await 只能用在 async 定义的协程函数里,普通函数用 await 直接报错
  • 律 2:await 后面只能跟可等待对象(协程对象、Task 对象、Future 对象),普通函数不能加 await

4.3 await 执行流程(超通俗)

  1. 执行到 await 任务 → 暂停当前任务
  2. 切换到事件循环里的其他可执行任务
  3. 等待的任务完成 → 切回原任务,继续执行后续代码
  4. 全程非阻塞,CPU 不空闲

4.4 代码拆解 await 执行顺序

import asyncio

async def demo():
    print("第一步:执行demo函数")
    # 遇到await,挂起demo,去执行其他任务
    await asyncio.sleep(1)
    # 等待结束,切回demo,继续执行
    print("第三步:await等待结束,继续执行")

async def main():
    print("第二步:执行main函数,调用demo")
    await demo()
    print("第四步:所有任务执行完毕")

asyncio.run(main())

执行顺序

第二步:执行main函数,调用demo
第一步:执行demo函数
第三步:await等待结束,继续执行
第四步:所有任务执行完毕

顺序解析
await demo() 的意思是:main 函数必须等 demo 函数从头到尾全部执行完,才能继续往下走第四步
在 demo 内部遇到 await asyncio.sleep(1) 时,demo 会被挂起等待 asyncio.sleep(1) 运行完毕并交出控制权,但因为 main 正在 await 它、没有其他任务可执行,程序只能空等 1 秒;等 sleep 结束后 切回demo 继续跑完,然后 main 才会执行第四步。
简单说:await 是“等它跑完”,不是“扔一边不管”

5 异步执行核心:事件循环与协程

5.1 协程(Coroutine)

被 async 修饰的函数就是协程函数,调用后得到的对象就是协程对象,协程的特点是可以被暂停和恢复,这也是异步能切换任务的核心。

5.2 事件循环(Event Loop)

loop = asyncio.get_event_loop()

异步程序的总指挥,负责:

  • 管理所有待执行的协程任务
  • 遇到 await 就挂起任务,切换其他任务
  • 监听等待任务的完成状态,完成后恢复执行

简单理解:事件循环就是一个“任务调度器”,来回切换协程任务,让程序非阻塞运行。

5.3 异步启动方式(新手必记)

Python 3.7+ 简化了异步启动,直接用 asyncio.run(主协程函数) 即可,自动创建和关闭事件循环,新手不用管复杂的循环创建逻辑。

6 常见坑点总结,新手避坑

  1. async 函数直接调用,不执行:忘记加 await,或没通过 asyncio. run 启动
  2. 普通函数加 await 报错:await 只能跟可等待对象,普通函数不能用
  3. await 放在普通函数里报错:await 必须在 async 函数内部
  4. 异步 sleep 用成 time. sleep:time. sleep 是同步阻塞,异步里必须用 await asyncio.sleep()
  5. 协程对象没执行完:忘记 await,导致任务没跑完程序就结束

7 总结

最后再用 3 句话,帮你彻底记住 async/await 核心:

  1. async:定义协程函数,直接调用不执行,返回协程对象
  2. await:挂起当前任务,切换执行其他任务,等待完成后恢复
  3. 异步核心:非阻塞、协程切换、事件循环调度,适合 IO 密集型场景(爬虫、网络请求、文件读写)

Python 异步编程看似复杂,其实弄懂执行逻辑和关键字作用,新手也能快速上手,尤其是写高并发爬虫、接口调用时,异步能大幅提升程序效率,赶紧动手试试吧!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐