尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
实例详解Python的进程,线程和协程
前言「进程、线程、协程」这三个词经常被放在一起讲但很多文章只讲怎么用不讲它们为什么长成现在这样。这篇不走「API 大全」路线而是拆开三者的机制进程的内存隔离、线程的共享内存与 GIL、协程在单线程里的协作式切换。先把一个常见误解说清楚它们不是「一代比一代先进」的替代关系。进程最重、隔离最彻底线程共享内存但有 GIL 限制协程最轻但只在 I/O 密集场景有意义。选哪个取决于你的任务卡在哪里。本文只讨论 CPython。文中「开销」「隔离」这些说法指的是机制层面的成因不会给出任何具体倍速数字——那需要读者在自己的机器上测量而结果随任务、版本、硬件差异极大写死一个数字没有意义。一、进程内存隔离与它的代价进程是操作系统分配资源的基本单位。每个进程有独立的虚拟地址空间一个进程里的全局变量、堆上的对象另一个进程默认看不到。这个隔离带来两个后果好处一个进程崩溃不会直接拖垮另一个数据不会被另一个进程悄悄改掉能真正用上多核——多个进程可以同时在不同 CPU 核心上执行 Python 字节码因为 GIL 是「每个解释器一个」进程之间各管各的。所以 CPU 密集任务要靠multiprocessing。代价进程之间不能直接访问对方的对象必须通过进程间通信IPC传数据而 IPC 通常意味着序列化与反序列化。CPython 的multiprocessing默认用pickle来搬运参数和返回值这就引出了它最容易踩的坑。# 适用于 Python 3.8Windows 下必须放在 if __name__ 保护内调用from multiprocessing import Pooldef square(x):return x * xif __name__ __main__: # Windows 用 spawn 启动子进程这行是必须的with Pool(4) as pool:print(pool.map(square, [1, 2, 3, 4]))这里有两道硬门槛Windows 用 spawn。Linux 上fork可以复制父进程内存Windows 没有fork只能新起一个解释器再重新导入模块、把要调用的函数和参数序列化过去。因此必须有if __name__ __main__:保护否则子进程导入主模块时会递归地再创建子进程直接炸掉。参数和返回值必须可 pickle。lambda、嵌套函数、打开的 socket、数据库连接、文件句柄都不能直接传。想传这些得改设计——比如传一个「可重建的配置」在子进程里自己建连接。「开销从哪来」的答案就在这两点进程创建本身要分配地址空间再加上每次调用都要带着数据走一趟 pickle。所以任务粒度太细时多进程反而更慢——序列化的成本盖过了并行的收益。二、线程共享内存与 GIL线程是 CPU 调度的基本单位同一进程内的多个线程共享同一份内存全局变量、堆对象、打开的文件都对彼此可见。这让线程间通信几乎零成本——不需要序列化直接读写同一个对象就行。但「共享」也正是危险的来源。多个线程同时改同一个变量中间可能被打断结果就错了。这就是竞态条件race condition。再说 GIL全局解释器锁。它是CPython 的实现细节不是 Python 语言的规定。官方词汇表的定义是GIL 是 CPython 用来保证同一时刻只有一个线程执行 Python 字节码的机制它简化了实现让dict等内置类型对并发访问隐式安全。由此得到两条必须记准的结论CPU 密集型任务多线程基本没有加速效果——因为任意时刻只有一个线程在跑字节码其他线程在排队等 GIL。想用多核并行要用multiprocessing或ProcessPoolExecutor。I/O 密集型任务多线程有效——线程在等待网络、磁盘时会释放 GIL别的线程就能趁这段时间跑起来。# 适用于 Python 3.8import threadingcounter 0lock threading.Lock()def worker():global counterfor _ in range(100000):with lock: # 没有这把锁 不是原子操作结果会偏小counter 1注意counter 1看着像一步实际包含「读—加—写」三步线程可能在中间被切换所以必须加锁。关于「Python 有没有去掉 GIL」CPython 3.13 起提供了实验性的自由线程free-threadedno-GIL构建需要专门安装、不是默认构建。而且去掉 GIL 并不等于自动变快——单线程性能可能变慢还需要代码自己处理好并发安全。所以不要说「Python 已经没有 GIL 了」主流发行版默认构建至今仍然有 GIL。三、协程单线程内的协作式切换协程coroutine跑在一个线程里由事件循环调度。它的切换是协作式的协程必须主动在await处让出控制权事件循环才可能去跑别的协程。没人能强行打断一个协程除非它自己 await 了。这带来两个直接后果它为什么轻没有线程创建/销毁的成本没有操作系统上下文切换的成本一个进程里可以同时「挂起」成千上万个协程——因为它们本质上只是内存里的对象不像线程那样每个都要有独立的栈和内核调度实体。它的边界在哪单个协程一旦执行了一段不含await的耗时计算整个事件循环就停住了其他协程全部等它。所以协程只对 I/O 密集有意义CPU 密集放在协程里只会把循环堵死。# 适用于 Python 3.8import asyncioasync def fetch(name, delay):print(f{name} 开始)await asyncio.sleep(delay) # 在这里让位别的协程可以跑print(f{name} 结束)return nameasync def main():# 三个协程几乎同时开始总耗时约等于最长的那个 delay而不是三者相加await asyncio.gather(fetch(A, 1),fetch(B, 1),fetch(C, 1),)asyncio.run(main())四、三者机制对照维度进程线程协程调度者操作系统操作系统事件循环用户态内存各自独立共享共享同一线程切换方式抢占式抢占式协作式靠 await切换开销大中小并行能力真并行可用多核受 GIL 限制无单线程数据传递需 IPC / 序列化直接读写共享对象直接读写无锁主要开销来源创建 pickle 搬运上下文切换 锁争用单个协程阻塞会让位失败适用场景CPU 密集I/O 密集高并发 I/O一句话概括三者的「开销从哪来」进程的开销主要在数据的序列化与复制以及独立的地址空间线程的开销主要在操作系统上下文切换和为了共享内存不得不加的锁协程的开销主要是内存里多存一个协程对象代价小但要求你严格避免阻塞调用。常见坑点以为多线程能让 CPU 密集任务变快❌ThreadPoolExecutor跑大量纯计算还期待用满多核✅ CPU 密集改用multiprocessing/ProcessPoolExecutorWindows 上多进程忘了if __name__ __main__:❌ 直接在主模块顶层写Pool(...)子进程导入时无限递归创建✅ 把所有多进程启动代码放进if __name__ __main__:保护块给子进程传了不可 pickle 的对象❌ 把 lambda、数据库连接、打开的文件当参数传给Pool.apply_async✅ 传可重建的配置在子进程内部创建连接在协程里跑同步阻塞代码❌async def f(): requests.get(url)✅ 用异步 HTTP 客户端或await asyncio.to_thread(...)3.9线程里无锁修改共享变量❌ 两个线程都对全局counter 1✅ 用threading.Lock保护临界区把「协程比线程快」当普遍规律❌ 用协程去跑 CPU 密集计算✅ 认清协程只在 I/O 等待时才有优势认为自由线程构建已经默认生效❌ 说「Python 3.13 已经没有 GIL 了」✅ 自由线程是 3.13 起的实验性非默认构建默认构建仍有 GIL总结你的瓶颈是该用别用CPU 计算多进程 / 进程池多线程、协程网络、磁盘 I/O多线程或协程串行同步调用超高并发连接协程一连接一线程选型的第一步永远是先判断任务卡在哪卡在 CPU就上进程卡在等待才轮到线程或协程。把 GIL 的作用记准、把进程的 spawn 与 pickle 约束记准剩下的就是看场景做取舍。
RELATED

相关推荐

Paritok 成本测算指南:从 25% 到 85% 的省钱曲线,团队一年能省多少钱

Paritok 成本测算指南:从 25% 到 85% 的省钱曲线,团队一年能省多少钱

【免费下载链接】paritok-4b-v1 Non-destructive compression gateway for AI coding agents. Cuts token bills 25% on turn 1 to past 85% in long or saturated sessions, and fits ~3 more turns in the same context window. Powered by our open-source code-native 4B m…

📅 2026/10/11 21:47:13
滑块验证码识别的YOLO缺口检测实战:从数据合成到部署推理

滑块验证码识别的YOLO缺口检测实战:从数据合成到部署推理

简介:基于 Python 的滑块验证码 Yolo 识别算法新版源码包,附有说明文档,主要面向计算机、数学、电子信息等专业学生,可支撑课程设计、期末大作业、毕业设计,也适合新手通过实际项目完成从数据处理到模型推理的完整演练…

📅 2026/10/11 21:47:13
仿贝壳房产系统源码二次开发:环境搭建、房源模块优化与权限设计

仿贝壳房产系统源码二次开发:环境搭建、房源模块优化与权限设计

简介:这是一套面向房产中介创业者、房产门户运营方及PHP开发者的开源房产系统网站源码,主打仿贝壳、链家、58同城等平台的业务模式,可一站式搭建新房、二手房、出租房、小区、问答等多场景房产电商平台。系统同时覆盖PC端与手机端&#xff0c…

📅 2026/10/11 21:42:13
MORE NEWS

更多资讯

📰

Agent技能工程:可验证、可监控、可复用的智能体能力单元设计

1. “agent-skills”不是新词,而是智能体能力工程的实践切口“agent-skills”这个词乍看像某个开源库的包名,或是某次技术分享里一闪而过的术语缩写。但过去两年在多个跨领域项目中反复遇到它——不是作为概念被宣讲,而是作为实际开发中必须拆…

📰

模塑玻璃瓶缺陷识别数据集:28类缺陷与YOLOv5实战

简介:这份资源是面向工业质检与计算机视觉方向的模塑玻璃瓶缺陷识别数据集,适合从事缺陷检测算法研发、YOLO模型训练及产线视觉方案验证的工程师与学习者使用。数据集覆盖黑点、泡泡颈、破损、刮痕、裂缝等28类常见玻璃瓶缺陷,标注信息完整&a…

📰

误差椭圆详解:从协方差阵到点位精度分析

1. 为什么笔记十二要单独写误差椭圆误差理论与测量平差基础这门课,大家最熟悉的肯定是协方差传播、权、条件平差、间接平差这些大块头。等这些基础过了之后,随之而来的一个非常实际的问题就是:平差算出的坐标点,到底有多可靠&…

📰

MySQL查询结果加序号全解析:从ROW_NUMBER到用户变量与分组排名

说实话,数据库开发里最容易被低估的需求,就是“给查询结果加个序号”。听起来不就是一列 1、2、3、4 吗?可真到动手写的时候,版本差异、排序稳定性、分页跳号、分组重排,随便一个细节都能让你在测试环境折腾半天。我这…

📰

森林害虫目标检测数据集实战:YOLOv8训练与避坑指南

简介:这份森林害虫目标检测数据集面向林业智能监测、农业AI应用开发及生态科研人员,聚焦松毛虫、松墨天牛、卷叶蛾三类常见且危害严重的森林害虫识别难题。数据集共1715张实际场景采集的JPEG图片,按训练集1199张、验证集257张、测试集259张划…

📰

时间序列研究全景解析:从基础模型到流匹配与VLM的实战指南

1. 时间序列研究的版图变迁与选题逻辑时间序列这个方向,这几年在顶会上的存在感肉眼可见地变强了。早些年做时序的人多少有点“边缘感”,投稿时经常被分到偏应用的track,审稿人一句“novelty不够”就能把工作打回去。但这两年情况完全反过来了…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬