Python 异步编程完全指南(一):初识异步编程

系列导航:[入门篇] → 核心概念篇实战案例篇高级技巧篇避坑指南篇


在这里插入图片描述

前言:为什么你必须掌握异步编程?

想象一下这个场景:你需要从 100 个网站抓取数据。

  • 传统同步方式:一个接一个地请求,每个请求等待 1 秒,总共需要 100 秒
  • 异步方式:同时发起 100 个请求,总共只需要 约 1 秒

效率提升 100 倍! 这就是异步编程的魔力。

在当今互联网时代,高并发、高性能已经成为应用的标配需求。无论你是:

  • 🕷️ 开发爬虫系统
  • 🌐 构建 Web 服务
  • 📊 处理实时数据流
  • 🤖 开发聊天机器人

异步编程都是你绑不开的必修课。

本系列将带你从零开始,彻底掌握 Python 异步编程的精髓。


一、理解异步:从生活场景说起

1.1 餐厅点餐的启示

假设你去餐厅吃饭:

同步模式(糟糕的餐厅)

服务员 → 接待客人A → 等待A点完餐 → 送餐给A → 等A吃完 → 接待客人B → ...

一个服务员一次只能服务一位客人,效率极低。

异步模式(高效的餐厅)

服务员 → 接待客人A → A看菜单时去接待B → B看菜单时去送餐给C → ...

服务员在等待时去做其他事情,效率大幅提升。

1.2 程序世界中的同步与异步

┌─────────────────────────────────────────────────────────────────┐
│                      同步执行模型                                │
├─────────────────────────────────────────────────────────────────┤
│  任务A ████████████                                             │
│  任务B             ████████████                                 │
│  任务C                         ████████████                     │
│  ─────────────────────────────────────────────────────► 时间    │
│  总耗时: 任务A + 任务B + 任务C = 30秒                            │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                      异步执行模型                                │
├─────────────────────────────────────────────────────────────────┤
│  任务A ████████████                                             │
│  任务B ████████████                                             │
│  任务C ████████████                                             │
│  ─────────────────────────────────────────────────────► 时间    │
│  总耗时: max(任务A, 任务B, 任务C) = 10秒                         │
└─────────────────────────────────────────────────────────────────┘

1.3 关键概念辨析

概念 含义 类比
同步 (Sync) 按顺序执行,等待完成 排队买票,必须等前面的人买完
异步 (Async) 不等待,继续做其他事 取号等候,可以先去逛街
并发 (Concurrent) 同一时间段处理多个任务 一个厨师同时炒几个菜
并行 (Parallel) 同一时刻执行多个任务 多个厨师同时炒菜
阻塞 (Blocking) 等待操作完成才能继续 打电话时必须等对方接听
非阻塞 (Non-blocking) 不等待,立即返回 发微信后不用等回复

💡 重要理解:Python 的 asyncio 实现的是单线程并发,通过事件循环在多个任务间切换,而不是真正的并行执行。


二、Python 异步编程基础

2.1 第一个异步程序

让我们从最简单的例子开始:

import asyncio

# 定义一个协程函数(使用 async 关键字)
async def say_hello():
    print("Hello")
    await asyncio.sleep(1)  # 异步等待 1 秒
    print("World")

# 运行协程
asyncio.run(say_hello())

输出

Hello
(等待 1 秒)
World

代码解析

  • async def - 定义一个协程函数
  • await - 暂停当前协程,等待异步操作完成
  • asyncio.run() - 运行协程的入口点

2.2 理解 async/await 语法

async def - 定义协程函数
# 普通函数
def regular_function():
    return "I'm regular"

# 协程函数(加上 async 关键字)
async def coroutine_function():
    return "I'm a coroutine"

# 调用对比
print(regular_function())        # 输出: I'm regular
print(coroutine_function())      # 输出: <coroutine object ...>  ← 返回协程对象!

⚠️ 注意:调用协程函数不会立即执行,而是返回一个协程对象,需要用 awaitasyncio.run() 来执行。

await - 等待协程完成
async def fetch_data():
    print("开始获取数据...")
    await asyncio.sleep(2)  # 模拟网络请求
    print("数据获取完成!")
    return {"status": "success"}

async def main():
    # await 会暂停当前协程,等待 fetch_data() 完成
    result = await fetch_data()
    print(f"结果: {result}")

asyncio.run(main())

2.3 同步 vs 异步:代码对比

场景:模拟 3 个耗时 1 秒的网络请求

同步版本
import time

def fetch_data(id):
    print(f"开始请求 {id}")
    time.sleep(1)  # 模拟耗时操作
    print(f"完成请求 {id}")
    return f"data_{id}"

def main():
    start = time.time()
    
    result1 = fetch_data(1)  # 等待 1 秒
    result2 = fetch_data(2)  # 再等待 1 秒
    result3 = fetch_data(3)  # 再等待 1 秒
    
    print(f"结果: {result1}, {result2}, {result3}")
    print(f"总耗时: {time.time() - start:.2f} 秒")

main()

输出

开始请求 1
完成请求 1
开始请求 2
完成请求 2
开始请求 3
完成请求 3
结果: data_1, data_2, data_3
总耗时: 3.01 秒
异步版本
import asyncio
import time

async def fetch_data(id):
    print(f"开始请求 {id}")
    await asyncio.sleep(1)  # 异步等待
    print(f"完成请求 {id}")
    return f"data_{id}"

async def main():
    start = time.time()
    
    # 使用 gather 并发执行多个协程
    results = await asyncio.gather(
        fetch_data(1),
        fetch_data(2),
        fetch_data(3)
    )
    
    print(f"结果: {results}")
    print(f"总耗时: {time.time() - start:.2f} 秒")

asyncio.run(main())

输出

开始请求 1
开始请求 2
开始请求 3
完成请求 1
完成请求 2
完成请求 3
结果: ['data_1', 'data_2', 'data_3']
总耗时: 1.00 秒

2.4 对比总结

指标 同步版本 异步版本 提升
总耗时 3.01 秒 1.00 秒 3 倍
代码复杂度 简单 稍复杂 -
资源利用率 -

三、动手练习

练习 1:Hello Async

编写一个异步函数,打印 “开始”,等待 2 秒,然后打印 “结束”。

import asyncio

async def hello_async():
    # 你的代码
    pass

asyncio.run(hello_async())
点击查看答案
import asyncio

async def hello_async():
    print("开始")
    await asyncio.sleep(2)
    print("结束")

asyncio.run(hello_async())

练习 2:并发倒计时

编写一个程序,同时启动 3 个倒计时器(分别从 3、2、1 开始),观察输出顺序。

import asyncio

async def countdown(name, n):
    # 你的代码
    pass

async def main():
    # 你的代码
    pass

asyncio.run(main())
点击查看答案
import asyncio

async def countdown(name, n):
    while n > 0:
        print(f"{name}: {n}")
        await asyncio.sleep(1)
        n -= 1
    print(f"{name}: 完成!")

async def main():
    await asyncio.gather(
        countdown("A", 3),
        countdown("B", 2),
        countdown("C", 1)
    )

asyncio.run(main())

输出

A: 3
B: 2
C: 1
A: 2
B: 1
C: 完成!
A: 1
B: 完成!
A: 完成!

四、本篇小结

本篇我们学习了:

  • 异步编程的意义:大幅提升 I/O 密集型任务的效率
  • 核心概念辨析:同步/异步、并发/并行、阻塞/非阻塞
  • 基本语法async def 定义协程,await 等待执行
  • 运行方式asyncio.run() 启动异步程序
  • 并发执行asyncio.gather() 同时运行多个协程

下篇预告

在下一篇 核心概念篇 中,我们将深入学习:

  • 事件循环 (Event Loop) 的工作原理
  • Task 对象的创建和管理
  • gather、wait、TaskGroup 的区别和使用场景
  • 协程的生命周期

系列导航:[入门篇] → 核心概念篇实战案例篇高级技巧篇避坑指南篇


如果这篇文章对你有帮助,欢迎点赞、收藏、关注!有问题欢迎评论区讨论。

Logo

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

更多推荐