别再乱用 Python 多线程了
GIL 的真相多线程比单线程还慢的现场复现不少人去学多线程, 觉得开启若干个线程便能够让程序加速。然而一旦运行, 却发觉多线程比单线程还要迟缓这并非是bug, 乃是GIL在从中作梗。今日借助代码于现场重现此现象, 以使你全然明晰 GIL 究竟是什么, 为何会致使多线程运行速度减缓, 何种场景应当运用多线程。先看现象多线程反而更慢写一个 CPU 密集型任务计算 1 亿次累加import threading import time def cpu_task(): count 0 for i in range(100000000): count 1 return count # 单线程 start time.time() cpu_task() cpu_task() print(f单线程耗时: {time.time() - start:.2f}s) # 多线程 start time.time() t1 threading.Thread(targetcpu_task) t2 threading.Thread(targetcpu_task) t1.start() t2.start() t1.join() t2.join() print(f多线程耗时: {time.time() - start:.2f}s)运行结果单线程耗时: 6.82s 多线程耗时: 7.15s # 反而更慢打开了两个线程, 从理论层面来讲应当快上一倍, 然而结果却是反倒变慢了, 这便是 GIL 的问题之所在。GIL 是什么GILLock, 它属于解释器的哪一把全局锁 , 在同一时刻, 某一个线程能够执行字节码 , 仅只有一个而已。即是说, 哪怕你开启了十组进程单元中央处理器存有八个核心枢纽, 也仍旧仅能借由一个核心枢纽来运行。其余的进程单元全皆处于排列队伍之中, 并等待着解锁标志存在。为什么多线程反而更慢多线程不仅没加速还多了额外开销CPU密集型的任务, 线程始终在争抢GIL, 抢到之后才被允许执行一阵子, 随后又不得不释放, 如此反复折腾, 从而比单线程还要更为缓慢。什么场景多线程有用对于 IO 密集型任务情形之下, 多线程是具备有用性的举例来说, 像是网络请求操作, 还有文件读写行为, 以及数据库操作事项。在线程等待IO之际, 会将GIL释放掉, 如此一来, 别的线程便能够借此机会去执行。import threading import time import requests def io_task(): requests.get(https://httpbin.org/delay/1) # 单线程 start time.time() io_task() io_task() print(f单线程耗时: {time.time() - start:.2f}s) # 多线程 start time.time() t1 threading.Thread(targetio_task) t2 threading.Thread(targetio_task) t1.start() t2.start() t1.join() t2.join() print(f多线程耗时: {time.time() - start:.2f}s)运行结果单线程耗时: 2.35s 多线程耗时: 1.21s # 快了近一倍CPU 密集型怎么办CPU 密集型任务用 多进程from multiprocessing import Process import time def cpu_task(): count 0 for i in range(100000000): count 1 # 多进程 start time.time() p1 Process(targetcpu_task) p2 Process(targetcpu_task) p1.start() p2.start() p1.join() p2.join() print(f多进程耗时: {time.time() - start:.2f}s)运行结果多进程耗时: 3.52s # 比单线程 6.82s 快了近一倍每个进程有独立的 GIL互不影响真正实现并行计算。总结选对工具牢记着, 多线程并非是无所不能的, GIL制约了CPU密集型任务的并行本事, 选得正确的工具, 才能够切实实现提速。
