之前大家经常说python的多线程是假的,这里我们详细分析python的多进程和多线程问题,并进行个总结:
- 多进程(Multiprocessing):是"真"的。 是货真价实的操作系统级别的多进程,可以利用多核 CPU 并行计算。
- 多线程(Multithreading):也是"真"的,但有"锁"。 它们确实是操作系统的原生线程,不是 模拟出来的"虚拟线程"。但是,由于 Python GIL(全局解释器锁) 的存在,同一时刻只能有一个线程在 CPU 上执行 Python 字节码。
为了彻底明白,我们需要把"虚拟"、"并发"和"并行"这几个概念拆解开来。
1. 多进程 (multiprocessing):真正的人多力量大
当你使用 multiprocessing 库时,Python 会向操作系统申请创建全新的进程。
- 它的本质:就像你手动打开了 4 个"命令提示符"窗口,分别运行了 4 个 Python 脚本。
- 资源 :每个进程都有自己独立的 Python 解释器、独立的内存空间、独立的 GIL。
- 结果 :因为大家都有自己的"地盘"和"解释器",所以它们可以同时在 CPU 的不同核心上跑。
- 结论 :这是真正的物理并行。如果你有 8 核 CPU,起 8 个进程,CPU 占用率可以直接飙到 100%。
2. 多线程 (threading):真的线程,被"锁"住的手脚
这里是你疑惑的重点。很多人说 Python 线程是"假的",其实这种说法是不准确的。
-
它的本质 :Python 的
threading模块调用的是操作系统底层的原生线程接口(比如 Linux 的 pthreads 或 Windows 的线程)。所以从操作系统调度层面看,它们是真线程。 -
为什么像"虚拟"的?(GIL 的锅)
CPython(我们常用的 Python 版本)在执行代码时,必须持有一把锁,叫 GIL (Global Interpreter Lock)。
-
规则 :任何时候,无论你开了多少个线程,同一个进程内,只能有一个线程持有 GIL 并在 CPU 上运行 Python 代码。
-
工作方式 :
- 线程 A 拿到锁,跑一会儿。
- 线程 A 释放锁(或者被迫暂停)。
- 线程 B 抢到锁,跑一会儿。
- 线程 B 释放锁。
- 这一切发生得非常快,看起来像是大家在同时动,但微观上是轮流动的。
-
-
结论 :这是并发(Concurrency) ,而不是并行(Parallelism)。对于纯计算任务(比如做数学运算),多线程不仅不会变快,反而因为频繁切换上下文变慢。
3. 既然多线程会被锁住,为什么还要用?
虽然多线程在"算术"上没用,但在等待上非常有用。
场景:I/O 密集型任务(爬虫、读写文件、网络请求)
想象你在做网络爬虫:
- 单线程 :发送请求 -> 傻等 1 秒服务器响应 -> 处理数据 -> 发送下一个。
- 多线程 :
- 线程 A 发请求 -> 遇到等待(I/O 阻塞) -> 自动释放 GIL 锁。
- 线程 B 拿到锁 -> 发请求 -> 遇到等待 -> 释放锁。
- 线程 C ...
在等待网络数据或磁盘读写时,Python 会释放 GIL。 这时候多线程就体现出优势了:虽然 CPU 还是只能处理一个人的活,但它可以在大家"排队等待"的时候去帮别人干活。
4. 所谓的"虚拟线程"是什么?
既然 Python 的 threading 是系统原生的,那有没有"虚拟"的?
有,那就是 协程(Coroutine) ,比如 Python 的 asyncio 库。
- 协程:完全由程序(用户态)控制,不需要操作系统参与调度。它是在一个线程里,通过代码逻辑来切换任务。这才是真正意义上的"轻量级/虚拟"并发。
总结:形象的比喻
假设你的 CPU 是一个只有 4 个炉灶的厨房。
-
多进程(Multiprocessing):
- 你雇佣了 4 个厨师,每人分到一个炉灶。
- 效果:4 个人同时炒菜,速度 x4。
- 代价:你要付 4 份工资,而且他们之间传递调料(数据)很麻烦,因为隔得远。
- 适用:切肉、剁排骨(CPU 密集型计算)。
-
多线程(Threading):
- 你只雇佣了 1 个厨师,但他要在 4 口锅之间来回跑。
- 由于 GIL 的存在:这个厨师同一秒钟只能握住一把锅铲。
- 如果任务是切菜(CPU 计算):他在 4 个案板间跑来跑去切,反而把时间浪费在跑路上了,不如老老实实切完一个再切下一个。
- 如果任务是炖汤(I/O 等待):锅盖盖上炖汤的时候(等待网络响应),他可以去另一口锅炒菜。这样效率就高了。
- 适用:炖汤、煮粥(爬虫、文件下载)。
-
协程(Asyncio):
- 还是 1 个厨师,但他经过了精密的时间管理大师训练,知道在那一滴油刚热的时候迅速换手,没有任何多余的动作。
所以,Python 的多线程不是假的,只是被"限速"了。