为什么需要异步

一、为什么需要异步?——同步的瓶颈

1.1 先看问题——同步代码在等什么?

 
# 同步版:下载 10 个 URL —— 一个卡住,后面全等
 
import time
 
import requests
 
def download(url):
 
    resp = requests.get(url)       # ① 发请求 → 等网络返回(这期间 CPU 闲置!)
 
    return resp.text
 
start = time.time()
 
for url in urls:                   # ② 串行:每个 URL 都要等上一个下载完
 
    data = download(url)
 
print(f"耗时: {time.time() - start:.1f}s")   # 10 个 URL → ~10s(每个 1s)
 
 
时间线(同步):
 
URL1: [====网络等待 1s====]
 
URL2:                        [====网络等待 1s====]
 
URL3:                                               [====网络等待 1s====]
 
      ↑ 每个请求期间 CPU 闲置——浪费!总时间 = 1s × 10 = 10s
 

CPU 和 I/O 的速度差距(数量级)

操作耗时相当于
CPU 执行一条指令~0.3 ns1 秒
内存读取~100 ns5 分钟
网络请求(同机房)~1 ms1 个月
磁盘读取~10 ms1 年
网络请求(跨洲)~100 ms10 年

一句话:I/O 在等的时候,CPU 完全可以干别的。同步代码的问题是——它在 I/O 等待期间把整个线程堵死了。

1.2 异步怎么做——“等待时切走”

 
时间线(异步):
 
URL1: [发请求][··········等网络··········][收数据]
 
URL2:        [发请求][··········等网络··········][收数据]
 
URL3:               [发请求][··········等网络··········][收数据]
 
      ↑ 等网络期间切去发其他请求——总时间 ≈ 1 个请求的时间!
 
 
# ✅ 异步版:同时下载 10 个 URL —— 总耗时 ≈ 最慢的那个
 
import asyncio
 
import aiohttp
 
async def download(url):
 
    async with aiohttp.ClientSession() as session:
 
        async with session.get(url) as resp:   # ① await 处暂时"挂起"
 
            return await resp.text()            # ② 等待期间,事件循环去执行其他协程
 
async def main():
 
    tasks = [download(url) for url in urls]
 
    results = await asyncio.gather(*tasks)     # ③ "并发"执行——实际是快速切换
 
asyncio.run(main())   # 10 个 URL → ~1s
 

关键区分:异步 ≠ 并行。异步是”一个线程在多个任务间快速切换”,并行是”多个线程同时在多个 CPU 核上跑”。asyncio 做的是前者。

1.3 什么时候用异步?——I/O 密集型 vs CPU 密集型

I/O 密集型CPU 密集型
典型场景Web 请求、数据库查询、文件读写、消息队列数学计算、图像处理、加密解密、ML 推理
CPU 状态大部分时间在等 I/O——CPU 闲置大部分时间在算——CPU 跑满
asyncio✅ 最佳场景❌ 没用——没 I/O 可等
多线程✅ 能用(但重)⚠️ Python 有 GIL,用不了多核
多进程❌ 杀鸡用牛刀✅ 最佳——绕过 GIL,用满多核

一句话讲清:“asyncio 解决的是 I/O 密集型并发——大量时间花在等待网络/磁盘,CPU 闲置。如果是 CPU 密集型,Python 应该用多进程绕开 GIL。“


速记卡(面试闪卡)

Q1:一句话讲清「为什么需要异步」到底是什么?

A:异步是在 I/O 等待时把线程”切走”去干别的事——同步代码在等网络/磁盘时把整个线程堵死。

Q2:一、同步的瓶颈 —— 怎么理解? —— 怎么理解?

A:同步下载 10 个 URL 是一个等完下一个,CPU 在等网络时全闲置。CPU 执行一条指令约 0.3ns,网络请求同机房约 1ms——差 6 个数量级。就像你烧水时傻站着等,水开前啥也不干。

Q3:二、异步怎么做:等待时切走 —— 怎么理解? —— 怎么理解?

A:异步用 await 在等待处”挂起”协程,事件循环转去执行别的协程,等网络回来了再回来。10 个 URL 总耗时≈最慢那个。好比烧水时去拖地,水开了再来关火——时间重叠了。

Q4:三、异步 ≠ 并行 —— 怎么理解? —— 怎么理解?

A:异步是”一个线程在多个任务间快速切换”(并发),并行是”多个线程在多核上同时跑”。asyncio 做前者:单线程靠协程让出 CPU。就像一个人同时盯多锅菜,不是多个人各看一锅。

Q5:四、I/O 密集 vs CPU 密集 —— 怎么理解? —— 怎么理解?

A:I/O 密集型(Web 请求、查库、读写文件)大部分时间在等,CPU 闲置——asyncio 最佳。CPU 密集型(计算、图像处理)CPU 跑满,异步没用,得用多进程绕开 GIL。选型看瓶颈在”等”还是”算”。

Q6:核心速记主线有哪些?

  • 同步:I/O 等待时线程被堵死,CPU 闲置

  • 异步:await 挂起协程,事件循环切去干别的

  • 异步是并发(单线程切换),不是并行(多核同时)

  • I/O 密集用 asyncio,CPU 密集用多进程

口诀

A:同步等待线程挡,

异步切走不空忙;

并发切换非并行,

IO 密集它最强。

相关链接