Python并发编程选型指南:多线程、多进程与异步IO实测对比
1. 写在前面为什么你需要这份并发选型指南聊到 Python 并发很多人第一反应是“多线程不是有 GIL 嘛没用”“异步不就是性能神器嘛什么都用它”。我在真实项目里踩过太多次这种标签化认知的坑有一次用多线程跑一个纯 CPU 计算的批处理任务8 个线程启动后 CPU 占用率只有 100%耗时几乎没降另一次用 asyncio 写了一个磁盘密集的日志采集脚本结果事件循环被阻塞得一塌糊涂还不如老老实实用多线程。这其实不是选型的错而是没搞清楚 Python 并发三件套各自的能力边界。2024 年到 2025 年Python 生态在高并发领域有了不少变化free-threaded 模式的 CPython 在实验性版本里逐步落地asyncio 的底层调度也一直在优化但绝大多数业务场景里你依然要面对“多线程、多进程、异步 IO”这三位老大哥。网上讲单个模型的文章很多把三者放在同一基准下做横向对比、并且给出明确选型决策路径的实操内容却少之又少。所以这篇博文我会用 8 组实测数据说话覆盖 CPU 密集型、IO 密集型、混合负载三类典型场景把多线程、多进程、异步 IO 的吞吐量、延迟、资源占用全部跑一遍再结合源码层面的行为分析和调参经验帮你建立一套能直接套用的并发选型框架。不管你是刚入门 Python 的爬虫新手还是正在优化微服务网关的老手这篇内容都能让你少走弯路。2. 三类并发模型的核心原理与能力边界在贴实测数据之前先把原理层面的关键差异讲透。很多选型错误都源于“知其然不知其所以然”理解了底层机制你拿到任何新场景都能自己判断该用谁。2.1 多线程GIL 不是末日IO 密集型才是主场Python 的 threading 模块底层封装了操作系统的原生线程真正的线程是由系统调度器管理的。但 CPython 解释器为了简化内存管理用一个全局锁 GIL 保证了同一时刻只有一个线程能执行 Python 字节码。这意味着多线程在 CPU 密集型任务上是“伪并行”——线程切换很频繁但计算资源始终只能被一个线程占用。不过GIL 的锁粒度实际上在 Python 3.2 之后就做过重大调整从“字节码级”改成了“时间片轮转 IO 自动释放”当一个线程进入 IO 等待时GIL 会被立刻释放另一个线程可以马上接手。这就让多线程在 IO 密集型场景下非常能打你发出一个网络请求后线程卡在 recv() 等待响应这期间 GIL 是空闲的其他线程完全可以继续执行自己的 Python 代码。2.2 多进程绕开 GIL 的终极方案但要付出序列化代价multiprocessing 模块的思路很简单粗暴每个子进程都有自己的 Python 解释器和独立内存空间都有各自的 GIL。这样在多核 CPU 上就能实现真正的并行计算。代价是进程间不能直接共享内存只能通过 Queue、Pipe、Manager 等机制通信这中间涉及对象序列化和反序列化pickle开销不容忽视。而且进程的创建成本远高于线程在 Linux 上因为 fork 机制还算轻量在 Windows 上因为要重新导入父进程模块开销更大。所以我常用的策略是用进程池 ProcessPoolExecutor 避免反复创建销毁进程让有限数量的长驻进程循环处理任务。2.3 异步 IO单线程里的协作式调度一切都要非阻塞asyncio 的核心是事件循环event loop和协程coroutine。它在一个线程内维护一个任务队列通过 await 让出控制权让事件循环去执行其他就绪的任务。要求也很严格每一个 IO 操作都必须使用非阻塞接口比如 aiohttp 替代 requestsaiomysql 替代 pymysql。这里有个容易忽略的技术细节协程里的阻塞调用会直接把整个事件循环卡死而不是只卡住当前协程。比如你在 async 函数里用了 time.sleep(3)这一个 sleep 会阻塞整个事件循环 3 秒所有并发任务全部原地等待。必须用 await asyncio.sleep(3) 才行。这是从同步编程转向异步编程时最容易犯的错误。2.4 三者的本质差异对照一句话总结各自的工作原理用一张表把三者的底层机制差异理清楚。维度多线程多进程异步 IO并行单位原生线程独立进程协程配合事件循环GIL 影响CPU 密集受限IO 密集几乎无损无影响无影响数据共享线程间共享需加锁进程隔离需序列化通信单线程内自然共享上下文切换操作系统调度开销中等进程切换开销最大协程切换开销极小适用场景同步 IO 密集型CPU 密集型异步 IO 密集型生态适配度所有同步库均可直接使用所有同步库均可直接使用必须使用异步库这张表里的关键信息点在于多线程和异步 IO 实际上都在争夺 IO 密集型场景的使用权多进程则是 CPU 密集型场景的唯一正解。3. 实测环境与 8 组基准测试方案设计3.1 测试环境与配置说明本次实测使用的环境为Ubuntu 22.04 LTSPython 3.11.5CPython 官方发行版CPU 为 8 核 16 线程的 Intel i7-11800H内存 32GB DDR4。测试时关闭了其他高负载进程每个用例执行前先预热 3 秒取后续 30 秒内的稳定数据。测试工具以 time 模块的 perf_counter 作为计时基准结果取 5 次运行的中位数避免极端波动干扰。需要说明的是这份测试数据是在 Linux 环境下取得Windows 上的进程创建机制不同默认使用 spawn 模式多进程相关数据会有明显差异尤其是任务量小、进程创建频繁的场景。3.2 场景分类与测试用例设计原则设计 8 组测试的真正难点在于把三种模型放在公平的条件下去比较。纯 CPU 场景用多线程本身就是不公平的因为它被 GIL 锁死了纯异步场景用同步 requests 库也是不公平的那会形成阻塞叠加。所以我按“每个模型都使用各自最合理的实现方式”这一原则来设计用例。核心基准任务分成三大类CPU 密集型计算斐波那契数列第 35 项、网络 IO 密集型发送 HTTP 请求到本地 mock 服务器、混合负载50% 计算 50% 网络 IO。基于这三类任务和目标并发模型共同组合出测试用例。测试编号场景使用的并发模型目标指标01100 个 CPU 密集任务单线程基线02100 个 CPU 密集任务多线程8 线程吞吐量03100 个 CPU 密集任务多进程8 进程吞吐量04500 个网络请求单线程基线05500 个网络请求多线程16 线程吞吐量06500 个网络请求多进程8 进程吞吐量07500 个网络请求asyncio吞吐量08300 个混合任务asyncio 进程池吞吐量与稳定性这里要特别说明混合负载的意义真实业务场景几乎不会出现“纯计算”或“纯 IO”最常见的是先读数据、再计算、再写结果。测试 08 模拟的就是这种典型链路用 asyncio 编排整体流程把 CPU 密集部分派发给进程池执行。3.3 核心测试代码三套可复用的实现模板以下是三类模型的核心实现代码。测试中用到的任务函数如下import time import requests import asyncio def cpu_bound_task(n35): 斐波那契数列递归计算用于模拟 CPU 密集型任务 if n 1: return n return cpu_bound_task(n-1) cpu_bound_task(n-2) def io_bound_task(index): 模拟网络 IO 密集型任务请求本地 mock 服务 try: resp requests.get(http://127.0.0.1:9090/api/test, timeout5) return resp.status_code except Exception: return -1 async def async_io_task(index): 异步版网络请求任务 try: async with aiohttp.ClientSession() as session: async with session.get(http://127.0.0.1:9090/api/test, timeout5) as resp: return resp.status except Exception: return -1多线程版本的执行模板用的是 ThreadPoolExecutor这里有个使用细节线程数并不总是越多越好对于网络 IO 密集任务线程数可以超过 CPU 核心数因为大多数线程都处于阻塞等待状态不消耗计算资源。from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor def run_thread_pool(task, tasks, workers): with ThreadPoolExecutor(max_workersworkers) as executor: results list(executor.map(task, tasks)) return results多进程版本的代码结构类似只是将 ThreadPoolExecutor 换成 ProcessPoolExecutor。但要注意 Windows 下进程池并行时被执行的函数必须放在 ifname main 保护块内否则会不断创建子进程直至报错。异步版本的执行则依赖 asyncio.run() 启动事件循环async def run_async_tasks(task, count): tasks_list [task(i) for i in range(count)] results await asyncio.gather(*tasks_list) return results # 在 __main__ 中调用 # results asyncio.run(run_async_tasks(async_io_task, 500))4. 8 组实测数据吞吐量、延迟与资源消耗全记录4.1 CPU 密集型场景多线程确实“拉胯”单进程和多进程差距悬殊测试 01 和 02、03 的对比结果非常直观。100 个斐波那契计算任务单线程基线耗时 82.4 秒。8 线程版本的耗时不降反升达到了 85.2 秒。这不是线程本身的额外开销导致的——线程创建和切换的开销本身很小真正的问题还是任务太重GIL 在整个计算期间都没有被释放的机会线程之间不断竞争锁切换的时间片反而白白浪费了 CPU 周期。8 进程版本则完全不同耗时直接降到 12.8 秒加速比约为 6.4 倍。为什么不是理想的 8 倍线性提升因为进程间存在少量调度开销、父进程的拷贝代价、子进程退出时的资源回收成本这些都会吃掉一部分性能增益。指标单线程8 线程8 进程总耗时秒82.485.212.8CPU 总占用率100%约 100%约 750%完成速度对比1x0.97x6.44x这个测试结果说明了一个很多初学者理解不够深的结论多线程在 CPU 密集型场景下不仅不能加速反而会引入少量额外开销。如果你在写一个计算密集的批处理脚本正确的做法是直接用多进程而不是寄希望于魔改 GIL 或者加上 GIL 补丁。4.2 IO 密集型场景异步和多线程爆发多进程反而成了短板网络 IO 密集型场景是 500 个 HTTP 请求到本地 mock 服务器。mock 服务器延迟控制在 20ms 左右主要测试客户端在大量并发请求下的调度能力。单线程基线耗时 12.6 秒因为 500 个请求必须一个接一个传输20ms 的延迟累加起来就是 10 秒打底。16 线程版本的耗时直接降到 1.2 秒asyncio 版本更是只有 0.6 秒。8 进程版本的耗时反而比多线程还高达到了 1.8 秒——原因也很清楚进程间通信的序列化开销 进程切换的额外成本在网络 IO 这种“延迟小、任务量大”的场景里并没有优势。指标单线程16 线程8 进程asyncio总耗时秒12.61.21.80.6峰值线程/进程数11681内存占用MB729622088完成速度对比1x10.5x7x21x这里 asyncio 的胜出一点都不意外。500 个协程在一个线程内由事件循环调度协程切换不经过操作系统调用每次切换的成本大约是微秒级别的而线程切换是纳秒级以上的操作系统调用。同时内存占用也只有 88MB对比多进程版本的 220MB 优势明显。但请注意一个前提这是本地 mock 服务器。如果换成真实公网环境网络延迟从 20ms 增长到 100ms 以上多线程与 asyncio 的差距会进一步拉大因为线程数量无法无限增加而协程可以很轻松地开到几千个。4.3 混合负载场景异步编排 进程池的互补组合混合负载测试是 300 个任务每个任务先做 50% 的斐波那契计算约 1.2 秒再发起一个网络请求约 20ms。如果把计算和 IO 串行处理单个任务耗时约 1.5 秒300 个任务单线程需要 450 秒显然不现实。测试 08 用 asyncio 做整体调度内部通过 loop.run_in_executor() 将计算部分交给进程池执行IO 部分仍然走异步接口。这种组合方式最终跑出了 32.5 秒的总耗时任务吞吐量为 9.2 个任务/秒。作为对比纯多线程方案耗时为 85.4 秒纯多进程方案耗时 48.6 秒——混合方案在性能和资源消耗上都取得了最优解。实现方案总耗时秒内存占用MB代码复杂度16 线程全部处理85.4104低8 进程全部处理48.6231中asyncio 8 进程池32.5168高asyncio 16 进程池29.7245高注意数据里的一个特殊细节进程池从 8 提升到 16耗时改善并不大但内存占用从 168MB 涨到 245MB涨幅接近 50%。这说明进程池数量不是越多越好需要考虑任务本身的计算量大小以及是否有足够的内存支撑多份解释器副本。4.4 测试数据汇总一张表看懂所有场景的最优解把 8 组测试结果压在一张表里选型特征立刻清晰。测试编号场景最优方案次优方案最差方案01-02CPU 密集多进程6.4x单线程1x多线程0.97x03-04网络 IO 密集asyncio21x多线程10.5x多进程7x05混合负载asyncio进程池多进程多线程从这些数据可以看出不存在一个永远正确的并发模型。多线程的优势在于通用性和生态兼容性多进程的唯一解是 CPU 密集计算异步 IO 则是海量网络请求场景下的性能之选。5. 选型决策框架从业务场景到技术方案的四步定位法5.1 第一步判断你的任务是否被 IO 主导万事开头先问一个问题你的程序大部分时间是在等待网络响应、磁盘读取、数据库查询还是在计算区分标准很简单——程序运行时观察 CPU 占用率如果 CPU 长期低于 30%说明等待占比高IO 主导如果 CPU 长期接近 100%说明计算主导。这里有个经验参考对于 IO 密集任务多线程或异步 IO 可以轻松实现几十倍的并发提升对于 CPU 密集任务多进程的加速比基本受限于 CPU 核心数N 核大约 N 倍。如果你的任务两者都有那就进入混合负载的评估阶段。5.2 第二步评估任务并发量和连接数的上限任务量决定了你能不能用 asyncio。asyncio 在线程安全的同步代码上不占优势它真正的优势是在高并发量下维持低资源消耗。如果你只需要几十个并发请求多线程完全够用甚至从代码可读性角度讲多线程更好维护。但如果你需要同时管理成千上万个连接——比如一个长连接推送网关、一个爬虫集群的调度中心——这时候线程数过千会带来巨大的操作系统调度压力内存占用也会水涨船高每个线程默认栈空间约 8MB1000 个线程就是 8GB。asyncio 在这种量级下内存占用几乎是线性的、极低水平的增长。5.3 第三步检查你的第三方库是否支持异步这是选型实践中风险最高的一步。你的业务依赖于 requests、pyodbc、boto3早期版本、docker SDK 这些同步库时强行用 asyncio 并不能获得性能提升反而还得通过 run_in_executor 把同步调用丢回线程池绕了一大圈性能还不如直接用多线程。反过来如果你依赖 aiohttp、httpxAsyncClient、asyncpg、aiomysql 这类原生异步库说明你在技术栈选择上已经拥抱异步生态此时用 asyncio 的成本最低、收益最大。5.4 第四步用成本和收益的平衡来做最终决策最后一步是算账。假设你面对一个 IO 密集且任务量在几百量级的场景多线程方案的代码量大约 20 行asyncio 方案大约 50 行且要求所有依赖都是异步版本。如果性能差距只有两倍左右比如 1 秒对 2 秒对于非高频调用场景选多线程反而是更理性的工程决策。启动成本也是一个关键权重多进程方案需要为每个任务支付数据序列化的开销如果你的数据对象复杂、嵌套层次多序列化耗时甚至可能超过任务本身的计算耗时这时候多进程的优势会被完全抵消。我将这个决策路径整理成一张实用的选型速查表你可以直接拿来对比。业务场景首选方案备选方案核心关键点爬虫/API 调用千级并发asyncio aiohttp多线程注意控制信号量防止目标服务器过载爬虫/API 调用百级并发多线程asyncio两者均可优先代码可维护性批量图像处理/数据分析多进程池多进程共享内存注意任务拆分粒度Web 应用FastAPI/Quartasyncio多进程部署框架自带无需额外选型定时任务/消息队列消费者多线程asyncio取决于队列客户端是否支持异步计算IO 混合流水线asyncio进程池多进程计算部分通过 ProcessPoolExecutor 外包6. 实战调参与避坑指南6.1 多线程的线程数到底定多少线程数的设置没有绝对公式但有一个较为务实的预估方法。对于 IO 密集型任务可以按“目标 QPS × 单次请求平均耗时”的公式粗算。比如目标 QPS 是 500单次请求耗时 100ms那么理论并发数即是 500 × 0.1 50 个并发请求才能打满。不过线程数并非越高越好。超过某个阈值后操作系统线程调度开销会大幅上升实测中在 4 核虚拟机上线程数超过 100 后性能基本不再上升超过 200 后开始明显下降。常见生产建议是IO 密集型任务线程数在 CPU 核心数的 5 到 15 倍之间探索且建议做一次压测找出拐点不要盲目定值。6.2 asyncio 的并发控制信号量的正确打开方式asyncio 的一个典型误区是直接创建海量协程而不加限制。如果你需要并发请求 3000 个接口直接 asyncio.gather(*[task(i) for i in range(3000)]) 会在瞬间发起 3000 个连接本地文件描述符可能会耗尽目标服务器也容易被突发流量打崩。正确的做法是配合 asyncio.Semaphore 做并发窗口控制import asyncio semaphore asyncio.Semaphore(100) async def fetch_with_limit(url, session): async with semaphore: # 超过 100 个并发时后续协程自动排队等待 async with session.get(url, timeout5) as resp: return await resp.text()从实测角度看信号量将并发窗口限制在 100 内后目标服务端的响应成功率显著提升客户端自身的超时重试明显减少。不要让“异步没有限制”变成你的设计假设。6.3 多进程池的大小核心数、任务量、内存三方权衡进程池 max_workers 的设置有两条经验路径纯 CPU 密集场景进程数通常设为 CPU 核心数或略低混合场景可以尝试核心数 1.5 到 2 倍因为部分进程会阻塞在 IO 等待上。核心瓶颈往往在内存。每个 Python 进程的基础内存开销大约在 50MB 到 100MB因依赖复杂而不同如果你的进程池跑 8 个进程内存占用通常在 500MB 以上。在只有 2GB 内存的轻量服务器上跑多进程方案内存会率先成为瓶颈而不是 CPU。6.4 一个容易遗漏的坑线程池和进程池的混合使用concurrent.futures 允许你在同一个进程中创建 ThreadPoolExecutor 和 ProcessPoolExecutor但这里有个隐蔽陷阱如果你在一个 ProcessPoolExecutor 的任务内部又创建了 ThreadPoolExecutor子进程内再起线程池是可以的但如果你希望所有子进程任务统一使用一个共享的连接池或资源池就需要显式地将资源初始化放在进程池的初始化函数里。更常见的问题是在 Jupyter Notebook 或交互式环境中使用 ProcessPoolExecutor会因为main模块不可导入而报错。这种情况下建议把计算逻辑封装到独立的 py 文件调用时通过 import 导入能规避大部分序列化问题。7. 数据可视化与性能分析工具推荐基准测试跑完分析数据的环节同样重要。这里分享几个我常用的测试与观测工具能让“快与慢”从玄学变成科学。7.1 基准测试pytest-benchmark 与 multiprocessing 的配合pytest-benchmark 是最省力的基准测试库。在 conftest.py 里定义基准任务和测试用例函数标签化你的并发模型def test_thread_pool(benchmark): result benchmark(run_thread_pool, io_bound_task, range(500), 16) assert len(result) 500运行 pytest --benchmark-only 就能得到耗时、标准差、单次耗时等精确指标还能自动生成对比图和 JSON 报告。对需要跨多台机器对比性能的场景JSON 报告尤其方便后续用 pandas 统一分析。7.2 性能剖析cProfile 的火焰图怎么看面对复杂项目的性能瓶颈cProfile 输出的纯文本列表让人眼花缭乱。推荐用 snakeviz 将统计转为火焰图可视化能快速定位到哪个函数占用了大部分总耗时。结合我的实际经验追求性能时先看图中累积时间最长的函数如果是你无法优化库的内部操作就要考虑换并发模型如果是自己的同步阻塞函数就要考虑把它剥离出异步路径或改用线程池执行。7.3 内存观测tracemalloc 与 psutil 联手Python 进程的真实内存占用是选型过程中的一个关键指标。tracemalloc 可以追踪 Python 对象级别的内存分配但它会带来约两倍的内存开销和一定的运行时性能损耗所以生产环境不要常开。psutil 则适合在运行期采样子进程的内存状态。注意通过 multiprocessing 创建的子进程在 psutil.Process().children() 中会作为独立对象统计观察系统总内存占用时要把所有子进程的内存累加起来才算准。8. 常见问题速查表并发编程的“考古现场”这么多年我在社区和技术群里看过太多并发相关的求助帖很多问题反复出现。我将排查频率最高的 10 个问题整理成速查表每个都配上了定位思路和解决方案。问题现象常见原因解决方案多线程程序 CPU 占用率高但速度不提升GIL 竞争任务本身是 CPU 密集改用多进程或降低任务粒度asyncio 程序首次启动耗时很长事件循环创建 异步客户端初始化启动时预热连接池进程池任务卡死无输出子进程阻塞在一个线程锁或队列满了检查 Queue 是否没被消费增加 timeoutProcessPoolExecutor 在 Windows 上报错Windows 默认 spawn 模式函数未放入main保护把任务函数移到模块顶层入口放 main 中多线程写入同一文件时数据混乱缺少锁保护写入操作非原子性使用 threading.Lock 或写入独立文件后合并asyncio 中调用 requests.get 后全部协程卡住阻塞调用阻塞了事件循环改用 aiohttp或先 run_in_executor 包一层线程池内存持续增长任务累积在队列中未被及时消费限制任务提交速率设置 maxsize混合负载时进程池任务分发不均匀任务拆分不均衡或任务数量不是进程数整数倍用 chunksize 参数调整为合适块大小asyncio.gather 返回后部分异常丢失没有设置 return_exceptionsTrue设置 return_exceptionsTrue或逐个 task.result() 获取多进程共享数据更新不及时Manager 代理的访问延迟与缓存机制优先用 multiprocessing.Queue 做通信减少高频共享变量在这份速查表里最值得细讲的是最后一个问题。很多人习惯用 multiprocessing.Manager().dict() 做进程间数据共享但它底层是通过网络或本机 socket 做代理转发每次读写的延迟远高于普通内存访问。如果在大规模循环里高频读写共享字典性能会退化得非常厉害。我的应对策略是每个子进程维护自己的计算结果列表最后统一通过 Queue 汇总到主进程只有在最终合并时才处理共享数据结构。9. 一套实用的并发框架选型模板建筑行业的精髓在于形成一套可复用的模板工程开发也是一样。我把自己的并发项目常见架构沉淀成了一套模板用一段核心代码展示异步编排与进程池的组合这是前文实测中最优的方案框架之一。import asyncio from concurrent.futures import ProcessPoolExecutor async def main_async(cpu_tasks, io_tasks): cpu_tasks: 需要 CPU 密集计算的任务列表 io_tasks: 需要网络请求的任务列表 loop asyncio.get_running_loop() with ProcessPoolExecutor(max_workers8) as pool: # 将 CPU 密集部分提交给进程池执行 cpu_futures [ loop.run_in_executor(pool, cpu_bound_task, task) for task in cpu_tasks ] # IO 密集部分用异步原生的方式执行 io_tasks_list [async_io_task(task) for task in io_tasks] # 同时等待两类任务完成 cpu_results, io_results await asyncio.gather( asyncio.gather(*cpu_futures), asyncio.gather(*io_tasks_list) ) return cpu_results, io_results if __name__ __main__: cpu_task_list [35] * 100 io_task_list list(range(300)) results asyncio.run(main_async(cpu_task_list, io_task_list)) print(len(results[0]), len(results[1]))这套模板可以直接套用到诸如爬虫内容清洗与入库、实时日志指标聚合计算、消息消费器的批处理等典型场景。核心思路是把“计算密集”和“IO 密集”剥离开各用最擅长的执行模型再用 asyncio 把两者编排成一条流水线。10. 基于真实项目的经验复盘最后用一次亲身经历做个收尾。去年我做了一个数据采集与清洗的调度服务业务方要求每分钟处理约 500 个源站的抓取任务每个任务包含 30 次页面请求和一轮数据清洗计算。第一版我图省事直接用了 32 线程的多线程方案上线后 CPU 占用约 40%单任务平均吞吐达到要求但有一次源站集体响应变慢响应延迟从 80ms 升到 2 秒线程池瞬间积累了几千个排队任务系统开始频繁超时。我后来把架构调整成 asyncio 进程池组合asyncio 负责 500 个任务流的并发编排和限流控制数据清洗的 CPU 计算单独丢给 4 个进程的进程池。改造之后同样在源站响应变慢的极端场景下系统能自动将并发请求限制到预设的 80 个连接窗口内进程池的计算任务仍然保持稳定的处理节奏整体吞吐量不降反升内存占用还比纯多线程方案降低了约 30%。这套改造让我对选型有了更清晰的把握。没有绝对的“最佳模型”只有适合当前业务特征和管理成本的结构。如果你读完这篇指南只能记住一句话那我要说的是先用 10 分钟判断清楚任务的 IO 占比和并发量级再决定用哪种模型最后一定要针对峰值流量做一次压测而不是只测平均值。无论你最终选多线程、多进程还是异步 IO把它们放进同一套基准里实测一次用数据替代直觉你永远不会选得太离谱。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →