Python 异步编程入门:用 asyncio 轻松实现并发任务

前言

在日常开发中,经常会遇到需要同时处理多个 IO 任务的场景,比如:

  • 同时请求多个接口
  • 批量下载文件
  • 并发查询数据库

如果使用传统的同步写法,任务只能一个接一个执行,总耗时就是所有任务耗时之和。而使用 Python 的 asyncio,可以在单线程内实现并发,大幅提升 IO 密集型任务的效率。

本文通过一个简单示例,带你快速入门 asyncio。


一、核心概念

asyncio 是 Python 标准库自带的异步 IO 框架,核心概念包括:

  • 协程(Coroutine) :用 async def 定义的函数
  • 事件循环(Event Loop):负责调度协程执行
  • 任务(Task):将协程包装成任务,交给事件循环调度
  • await:挂起当前协程,等待异步操作完成

简单理解:遇到 IO 等待时,await 会让出控制权,让其他任务先执行。


二、同步 vs 异步

假设有三个下载任务,分别耗时 2 秒、1 秒、3 秒。

同步执行总耗时约:

text 复制代码
2 + 1 + 3 = 6 秒

异步并发执行总耗时约:

text 复制代码
max(2, 1, 3) = 3 秒

下面用代码验证。


三、完整示例

python 复制代码
import asyncio
import time


async def download(name: str, delay: float):
    print(f"[{name}] 开始下载...")
    await asyncio.sleep(delay)  # 模拟网络 IO
    print(f"[{name}] 下载完成,耗时 {delay}s")
    return f"{name} 的结果"


async def main():
    start = time.perf_counter()

    tasks = [
        download("文件A", 2),
        download("文件B", 1),
        download("文件C", 3),
    ]

    results = await asyncio.gather(*tasks)

    end = time.perf_counter()
    print("执行结果:", results)
    print(f"总耗时: {end - start:.2f}s")


if __name__ == "__main__":
    asyncio.run(main())

运行结果:

text 复制代码
[文件A] 开始下载...
[文件B] 开始下载...
[文件C] 开始下载...
[文件B] 下载完成,耗时 1s
[文件A] 下载完成,耗时 2s
[文件C] 下载完成,耗时 3s
执行结果: ['文件A 的结果', '文件B 的结果', '文件C 的结果']
总耗时: 3.00s

可以看到,总耗时约等于最慢的任务,而不是所有任务之和。


四、控制并发数量

如果任务太多,一次性全部并发可能会导致资源耗尽。可以使用 Semaphore 限制并发数。

python 复制代码
import asyncio
import time

sem = asyncio.Semaphore(2)  # 最多同时执行 2 个任务


async def download(name: str, delay: float):
    async with sem:
        print(f"[{name}] 开始下载...")
        await asyncio.sleep(delay)
        print(f"[{name}] 下载完成")
        return name


async def main():
    start = time.perf_counter()
    tasks = [download(f"文件{i}", 1) for i in range(1, 6)]
    results = await asyncio.gather(*tasks)
    end = time.perf_counter()
    print("结果:", results)
    print(f"总耗时: {end - start:.2f}s")


if __name__ == "__main__":
    asyncio.run(main())

五、常见坑

  1. 忘记加 await

    协程函数调用后不会立即执行,必须用 await 或包装成 Task。

  2. 在协程中使用 time.sleep()

    time.sleep() 会阻塞整个事件循环,应该使用 await asyncio.sleep()。

  3. CPU 密集型任务不适合 asyncio

    asyncio 适合 IO 密集型任务。如果是 CPU 密集型,建议使用多进程。


六、总结

asyncio 是 Python 中非常实用的异步编程工具,特别适合爬虫、API 聚合、文件下载等 IO 密集型场景。

记住三个关键词:

  • async def 定义协程
  • await 等待异步操作
  • asyncio.gather 并发执行多个任务

掌握这些,就能写出高效又简洁的异步代码。

相关推荐
拖累斯7 分钟前
Jenkins + Python 全自动跑通「禅道任务/BUG 导出 → 月度开发考核表」
python·jenkins·bug
j7~8 分钟前
【C++ 八股文】(篇四)C++11 新特性
开发语言·c++·面试·c++11·八股文
eggwyw22 分钟前
C++中大括号的用法合集
开发语言·c++
xiaoye-duck24 分钟前
《C++ 项目:手写大模型接入 SDK》本地 Ollama 部署与适配
开发语言·网络·c++·语言模型
特立独行的猫A35 分钟前
13MB 单文件、一条命令变服务:把仓颉写的 cjh 内嵌进你的应用,当它的 AI 大脑
java·后端·python
deepseek2343 分钟前
Agent任务中途断线,怎么避免重复执行?SQLite检查点实测
python·sqlite·任务调度·幂等性·ai agent
联犀1 小时前
联犀 Go SIMD 实战:视频转码从 16.5ms 到 5.46ms
开发语言·golang·音视频
octopus_c1 小时前
STL 之 string:auto 与范围 for、常用接口、模拟实现与深浅拷贝
开发语言·c++·算法
Nerd(BUG版)1 小时前
KMP学习day5--Kotlin Flow异步流基础
开发语言·学习·kotlin
J.YA1 小时前
String字符串[面试题]
java·开发语言