为什么Python异步请求比多线程快?生产环境该如何选型?

大萱姑娘_6364

大萱姑娘_6364

2026-09-23

705人浏览

原创

asyncio在高并发i/o场景下性能远超多线程,因其避开os线程调度与内存硬瓶颈:1000个线程耗内存超200mb且易触发oom,而1000个协程仅约10mb;协程切换在用户态、开销纳秒级,线程切换需内核态、成本高数个数量级。

为什么python异步请求比多线程快?生产环境该如何选型?

异步请求比多线程快,不是因为“协程本身更快”,而是因为在高并发 I/O 场景下,asyncio 避开了操作系统线程调度和内存开销的硬瓶颈——线程数一上几百,OSError: can't start new thread 或被 OOM killer 杀掉是常态;而协程轻松跑几千个,常驻内存仅 20–30MB。

为什么 async/await 在 I/O 并发时压倒 threading?

核心差异不在语法,而在执行模型和资源占用:

  • threading.Thread 每个线程默认占 8MB 栈空间(Linux),1000 个线程 ≈ 8GB 虚拟内存,实际 RSS 常超 200MB;asyncio 协程栈在堆上动态分配,单个协程仅几 KB,1000 个协程内存通常
  • 线程切换必须进内核,保存寄存器、页表、栈指针等,耗时从数百纳秒到微秒级;协程切换纯用户态,只保存局部变量和指令位置,本质是 generator 状态机跳转,开销可忽略
  • 线程在 socket.recv()time.sleep() 时陷入内核等待,期间操作系统仍要维护其上下文;协程 await 后直接让出控制权给事件循环,不占调度槽位
  • GIL 对两者影响不同:线程在 I/O 时释放 GIL,能并发;但协程根本不在意 GIL——它压根没多线程,所有代码都在一个线程里顺序执行,无锁竞争,也无自动共享

requests + threading 和 aiohttp + asyncio 的实操差距

同样发 100 个耗时 2 秒的 HTTP 请求(如本地 Flask 慢接口),两套写法效果截然不同:

A Python CLI skill for Cutout.Pro visual APIs — background removal, face cutout, and photo enhancement. Supports file upload & image URL input.
A Python CLI skill for Cutout.Pro visual APIs — background removal, face cutout, and photo enhancement. Supports file upload & image URL input.

调用 Cutout.Pro 视觉处理 API 进行背景移除、人像抠图和照片增强,支持文件上传与图片 URL 输入。

下载
  • requests.get() + ThreadPoolExecutor(max_workers=32):总耗时约 6–8 秒(受限于线程池大小和系统线程创建成本),ps -o pid,vsz,comm -p $(pidof python) 显示 VSZ 超 500MB
  • aiohttp.ClientSession.get() + asyncio.gather(*tasks):总耗时稳定在 ~2.1 秒(受最慢请求拖累),VSZ 通常
  • 关键陷阱:requests 在协程里直接调用会阻塞整个事件循环——它不是异步库。必须用 aiohttphttpx.AsyncClient 或显式用 loop.run_in_executor() 包裹 requests(但失去大部分优势)

生产环境选型不能只看“快”,要看任务混合度

真实服务 rarely 是纯 I/O 或纯 CPU,得拆开看:

  • 纯 I/O 密集(如 API 网关、爬虫、实时通知推送):无条件选 asyncio + 异步生态(aiohttphttpxaioredis)。注意:DB 驱动也得是异步的(如 asyncpgaiomysql),否则 await 一行就卡死
  • CPU 密集 + I/O 混合(如图片缩放+上传、日志解析+上报):主线程用 asyncio 处理网络,CPU 工作用 loop.run_in_executor() 丢给 concurrent.futures.ProcessPoolExecutor;别用 ThreadPoolExecutor,GIL 让它白忙
  • 强一致性状态管理(如计费、库存扣减):协程间不共享状态,global counter; counter += 1 是竞态灾难。要么用 asyncio.Lock,要么把临界区逻辑下沉到原子 DB 操作(如 UPDATE ... SET stock = stock - 1 WHERE stock >= 1

最容易被忽略的一点:异步不是银弹。你写的每个 await 点,都是潜在的调度让出点——如果某次 await db.fetch() 因慢查询卡住 5 秒,整个事件循环就停 5 秒。监控得盯紧协程平均延迟和事件循环滞留时间(asyncio.base_events._run_once 耗时),而不是只看 CPU 使用率。

Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1551

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

3684

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1569

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

21137

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2607

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2667

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1083

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

576

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2063

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程