❝Python入门第三十七课,主要是学习了解了全局解释器锁GIL,GIL是CPython的全局锁,确保同一时刻只有一个线程执行字节码,简化内存管理但限制了多线程的CPU并行能力。
一、GIL是什么?
全称:Global Interpreter Lock(全局解释器锁)。
概念:GIL 是 CPython 解释器中的一把互斥锁。
作用:无论 CPU 有多少个核心,在某一时刻,只允许同一个进程中的一个线程去执行 Python 代码,保护 Python 对象内部状态不被并发修改,简化内存管理(尤其是引用计数)。
范围:每个 Python 进程拥有一个独立的GIL(多进程不受影响)。
实现:CPython 核心的一个互斥锁,线程在运行 Python 代码前必须获得 GIL,执行完毕后释放(或主动让出)。
二、为什么 CPython 要有 GIL?
1、简化内存管理(核心原因)
Python 使用引用计数来管理内存。每个对象都有一个计数器,记录被引用的次数。如果没有GIL,多个线程可能同时修改同一个对象的引用计数,导致计数错误、内存泄露或重复释放。
2、避免复杂的细粒度锁
如果不使用 GIL,解释器需要为每个对象(整数、列表、字典等)加锁,这会造成:
3、历史原因
Python 早期设计(1990年代)时多核 CPU 并不普及,选择 GIL 是一种务实且高效的工程决策。时至今日,许多 C 扩展库(如 NumPy)依赖 GIL 的简洁性,完全移除 GIL 将破坏大量现有代码。
4、如果没有 GIL 锁,那么 Python 底层就可能会出现引用计数错误,导致内存“爆炸”。

三、GIL 如何工作?
基本流程
⒈线程启动时,会尝试获取 GIL。
⒉获得 GIL 的线程开始执行 Python 字节码。
⒊以下情况会释放 GIL:
- 线程执行
I/O操作(如time.sleep()、socket.recv()、file.read())时,会主动释放。 - 线程执行了固定数量的字节码指令(默认100条,可通过
sys.setswitchinterval()调整时间间隔),会强制释放。
⒋释放GIL 后,操作系统会调度另一个等待的线程获得 GIL。
关键点
- I/O操作:Python 大多数 I/O 函数会主动释放GIL,因此多线程在 I/O 密集型场景下能真正并发(因为等待 I/O 时其他线程可以运行)。
- CPU 密集型操作:纯 Python 计算不会主动释放 GIL,只能靠时间片抢占。但 GIL 的存在使得多个 CPU 核心无法同时执行 Python 字节码,因此多线程 CPU 任务反而可能因锁竞争变慢。
四、如何绕过 GIL 的限制?
1、针对 CPU 密集型任务:
多进程替代多线程:
- 使用
multiprocessing模块创建独立进程,每个进程有独立的 GIL 和内存空间,可真正并行利用多核。

调用C/C++扩展:
- 在
NumPy、Numba等库中,底层计算会主动释放 GIL,使多线程能并行执行(如矩阵运算)。
2、针对 I/O 密集型任务:
线程池优化:
- 使用
concurrent.futures.ThreadPoolExecutor管理线程,避免手动创建过多线程导致切换开销。
异步编程(asyncio):
- 通过事件循环在单线程内高效调度I/O操作,避免线程切换成本,适合高并发网络服务。
五、GIL 对比 Lock/RLock

结论:GIL 为了确保 CPython 解释器级别的数据安全,作为日常编码来说,我们对 GIL 是无感的,但对于Lock/RLock是实际编码中使用较多的,Lock/RLock是为了确保业务逻辑的完整。下面介绍两个Lock/RLock的示例。
示例一:让打印是完整的。
import timefrom threading import Thread, RLock, current_threaddefshow_info1(lock):for _ in range(10):with lock: print(f'\t {current_thread().name} AA', end='') print('AA', end='') print('AA') time.sleep(0.01)defshow_info2(lock):for _ in range(10):with lock: print(f'\t {current_thread().name} OO', end='') print('OO', end='') print('OO')if __name__ == '__main__': lock = RLock() t1 = Thread(target=show_info1, args=(lock,), name='show_info1') t2 = Thread(target=show_info2, args=(lock,), name='show_info2') t1.start() t2.start()
示例二:不要让两个窗口卖出同一张票。
import timefrom threading import Thread, RLock, current_threadcurrent = 1defsale(lock):global currentwhileTrue:with lock:if current <= 20: print(f'{current_thread().name}出售了第{current}张票!') current += 1else: print('票已售空')break time.sleep(0.3)if __name__ == '__main__': lock = RLock() t1 = Thread(target=sale, name='窗口1', args=(lock,)) t2 = Thread(target=sale, name='窗口2', args=(lock,)) t3 = Thread(target=sale, name='窗口3', args=(lock,)) t1.start() t2.start() t3.start()
上面示例,如果不使用锁,运行结果则会出现两个窗口售出了同一张票。