请添加图片描述

在现代编程中,异步编程(Asynchronous Programming) 是提升程序并发性能的关键技术之一。
无论是高并发的 Web 服务、网络爬虫,还是 I/O 密集型任务,异步编程都能极大提升运行效率。

本文将带你从零开始理解 Python 的异步编程模型,包括:

  • 什么是异步?
  • 为什么要使用异步?
  • Python 的 async / await 语法
  • asyncio 的使用
  • 常见陷阱与最佳实践


一、什么是异步(Asynchronous)?

举个例子
假设你要煮饭,同时还要洗衣服。

  • 同步(Synchronous):煮饭 → 等待煮饭结束 → 洗衣服
  • 异步(Asynchronous):煮饭开始 → 同时洗衣服 → 两件事同时进行

在计算机中,这就类似于:

当一个任务在等待 I/O(例如文件读取、网络请求)时,CPU 不再闲着,而是去执行其他任务。


二、为什么要使用异步?

Python 中的异步主要为了解决 I/O 密集型任务效率低 的问题,比如:

  • 网络请求(如爬虫、API 调用)
  • 文件或数据库读写
  • Web 服务器的高并发处理

在这些场景中,程序的大部分时间都在等待,而不是计算。
异步编程能在等待期间调度其他任务,从而实现“并发执行”。


三、同步 vs 异步 示例对比

同步写法

import time
def task(name):
print(f"{name} 开始")
time.sleep(2)
print(f"{name} 结束")
def main():
task("任务A")
task("任务B")
main()

输出:

任务A 开始
任务A 结束
任务B 开始
任务B 结束

总耗时约 4 秒


异步写法

import asyncio
async def task(name):
print(f"{name} 开始")
await asyncio.sleep(2)
print(f"{name} 结束")
async def main():
await asyncio.gather(task("任务A"), task("任务B"))
asyncio.run(main())

输出:

任务A 开始
任务B 开始
任务A 结束
任务B 结束

总耗时约 2 秒,性能提升一倍 !


四、Python 异步的核心语法

Python 3.5 引入了两个关键字:

  • async —— 定义异步函数
  • await —— 等待异步任务的完成

1. async:定义协程函数(coroutine)

async def hello():
print("Hello, async world!")

这个函数不会立即执行,而是返回一个 协程对象(coroutine object)

print(hello())
## 输出结果:<coroutine object hello at 0x7f67e693fa40>

2. await:挂起等待另一个异步操作完成

await 只能在 async 函数中使用,它的作用是:

暂停当前协程,等被等待的异步任务执行完再继续。

示例:

import asyncio
async def hello():
print("Start...")
await asyncio.sleep(2)
print("End!")
asyncio.run(hello())

输出:

Start...
(2 秒后)
End!

五、asyncio 核心概念

Python 标准库提供了异步框架 asyncio,用于管理和调度协程。

核心组成:

组件说明
asyncio.run()启动事件循环
asyncio.create_task()创建任务对象(task)
await挂起当前协程等待结果
asyncio.gather()并发执行多个协程

示例:并发执行多个任务

import asyncio
async def work(name, seconds):
print(f"{name} 开始工作...")
await asyncio.sleep(seconds)
print(f"{name} 完成!")
async def main():
task1 = asyncio.create_task(work("任务1", 2))
task2 = asyncio.create_task(work("任务2", 3))
await task1
await task2
asyncio.run(main())

输出:

任务1 开始工作...
任务2 开始工作...
任务1 完成!
任务2 完成!

六、asyncio.gather() 同时执行多个协程

如果希望简化写法,可以用 asyncio.gather() 一次性启动多个任务:

import asyncio
async def download(name, delay):
print(f"{name} 开始下载...")
await asyncio.sleep(delay)
print(f"{name} 下载完成!")
async def main():
await asyncio.gather(
download("文件A", 2),
download("文件B", 3),
download("文件C", 1)
)
asyncio.run(main())

输出(近似):

文件A 开始下载...
文件B 开始下载...
文件C 开始下载...
文件C 下载完成!
文件A 下载完成!
文件B 下载完成!

总耗时约 3 秒(最长的任务)。


七、异步与多线程、多进程的区别

特性异步(asyncio)多线程(threading)多进程(multiprocessing)
核心思想单线程事件循环多线程并行多进程并行
适用场景I/O 密集型I/O 密集型CPU 密集型
是否占用多个 CPU否(受 GIL 限制)✅ 是
上下文切换轻量(协程)较重最重
代码复杂度中等较高较高

总结:

  • 异步(asyncio)适合 I/O 密集型任务
  • 多进程适合 CPU 密集型任务

八、常见异步陷阱

❌ 1. 在普通函数中使用 await

await asyncio.sleep(1)
# SyntaxError: 'await' outside async function

✅ 正确做法:

async def main():
await asyncio.sleep(1)

❌ 2. 忘记使用 await

async def main():
asyncio.sleep(1)
print("Done!")

这段代码不会等待 sleep 完成,而是直接打印。
✅ 应写为:

await asyncio.sleep(1)

❌ 3. 忽略事件循环(loop)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

在 Python 3.7+ 中更推荐使用:

asyncio.run(main())

九、实战:异步爬虫示例

import asyncio
import aiohttp  # 异步HTTP库
urls = [
"https://example.com",
"https://www.python.org",
"https://pytorch.org"
]
async def fetch(session, url):
async with session.get(url) as response:
print(f"{url} 状态码:{response.status}")
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
await asyncio.gather(*tasks)
asyncio.run(main())

输出:

https://example.com 状态码:200
https://www.python.org 状态码:200
https://pytorch.org 状态码:200

仅需几百毫秒即可完成多个请求!


十、最佳实践总结

写异步函数

async def func(): ...

调用异步函数

await func()

运行异步程序

asyncio.run(main())

并发执行多个任务

await asyncio.gather(task1(), task2(), task3())

I/O 密集型推荐异步,CPU 密集型推荐多进程


如果想更深一步了解 Python 的异步操作,请参考深入理解 Python asyncio:事件循环与 Task 调度机制