# Python 全局解释器锁 (GIL) 详细解析
Python 全局解释器锁 (GIL) 详细解析
什么是 GIL?
全局解释器锁(Global Interpreter Lock,简称 GIL)是 Python 解释器(主要是 CPython)中的一个互斥锁,它确保在任何时刻只有一个线程能够执行
Python 字节码。这个机制存在于 CPython 解释器中,但并不存在于 Jython、IronPython 或 PyPy-STM 等其他 Python 实现中。
GIL 的工作原理
GIL 的核心机制如下:
- 单线程执行:即使在多核处理器上,CPython 解释器也只允许一个线程执行 Python 字节码
- 线程切换:Python 线程在执行一定数量的字节码指令或遇到 I/O 操作时会释放 GIL
- 强制释放:Python 解释器会定期强制释放 GIL,让其他线程有机会执行
# -*- coding: utf-8 -*-
import threading
import time
def count_down(n):
while n > 0:
n -= 1
print("检查是否输入数据", 50000000 * 2 / 100000000)
# 单线程执行
start = time.time()
count_down(100000000)
print(f"单线程耗时: {time.time() - start:.4f}秒")
# 多线程执行
threads = []
start = time.time()
for i in range(2):
t = threading.Thread(target=count_down, args=(50000000,))
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"双线程耗时: {time.time() - start:.4f}秒")
# 检查是否输入数据 1.0
# 单线程耗时: 1.8081秒
# 双线程耗时: 1.7343秒
在上述代码中,由于 GIL 的存在,双线程版本可能比单线程版本更慢,因为线程切换带来了额外开销。
GIL 存在的原因
GIL 主要出于以下考虑:
- 简化内存管理:Python 使用引用计数进行内存管理,GIL 确保引用计数的修改是线程安全的
- 保护 C 扩展:许多 C 扩展库不是线程安全的,GIL 提供了保护
- 历史原因:GIL 在 Python 早期就被引入,移除它需要大量工作
GIL 对并发的限制
1. CPU 密集型任务
对于 CPU 密集型任务,GIL 严重限制了多线程的性能提升:
# -*- coding: utf-8 -*-
import threading
import time
def cpu_bound_task():
count = 0
for i in range(10 ** 7):
count += i
# 单线程
start = time.time()
cpu_bound_task()
print(f"单线程 CPU 密集型任务: {time.time() - start:.4f}秒")
# 多线程
threads = []
start = time.time()
for _ in range(2):
t = threading.Thread(target=cpu_bound_task)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"双线程 CPU 密集型任务: {time.time() - start:.4f}秒")
# 单线程 CPU 密集型任务: 0.2602秒
# 双线程 CPU 密集型任务: 0.4866秒
2. I/O 密集型任务
对于 I/O 密集型任务,GIL 的影响较小,因为在等待 I/O 操作时会释放 GIL:
# -*- coding: utf-8 -*-
import threading
import time
import requests
def io_bound_task():
response = requests.get('https://www.python.org')
return len(response.content)
# 单线程
start = time.time()
for _ in range(5):
io_bound_task()
print(f"单线程 I/O 密集型任务: {time.time() - start:.4f}秒")
# 多线程
threads = []
start = time.time()
for _ in range(5):
t = threading.Thread(target=io_bound_task)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"五线程 I/O 密集型任务: {time.time() - start:.4f}秒")
# 单线程 I/O 密集型任务: 1.4823秒
# 五线程 I/O 密集型任务: 0.4828秒
绕过 GIL 限制的方法
1. 使用多进程
# -*- coding: utf-8 -*-
import multiprocessing
import time
def cpu_bound_task():
count = 0
for i in range(10 ** 7):
count += i
# 多进程
processes = []
start = time.time()
for _ in range(2):
p = multiprocessing.Process(target=cpu_bound_task)
processes.append(p)
p.start()
for p in processes:
p.join()
print(f"双进程 CPU 密集型任务: {time.time() - start:.4f}秒")
# 双进程 CPU 密集型任务: 0.0427秒
2. 使用 C 扩展
编写 C 扩展可以在执行期间释放 GIL:
// 示例 C 扩展代码
static PyObject* my_function(PyObject* self, PyObject* args) {
// 释放 GIL
Py_BEGIN_ALLOW_THREADS
// 执行 CPU 密集型操作
// ...
// 重新获取 GIL
Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
3. 使用异步编程
对于 I/O 密集型任务,可以使用 asyncio:
# -*- coding: utf-8 -*-
import asyncio
import aiohttp
import time
async def fetch_url(session, url):
async with session.get(url, verify_ssl=False) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, 'https://www.python.org') for _ in range(5)]
await asyncio.gather(*tasks)
start = time.time()
asyncio.run(main())
print(f"异步 I/O 密集型任务: {time.time() - start:.4f}秒")
# 异步 I/O 密集型任务: 1.3281秒
GIL 的未来
Python 社区一直在讨论移除 GIL 的可能性:
- PyPy-STM:已经实现了无 GIL 的版本,但尚未成熟
- Python 3.13:计划引入一个可选的无 GIL 模式
- PEP 703:提出了使 GIL 成为可选的提案
总结
GIL 是 CPython 解释器中的一个重要机制,它简化了内存管理和 C 扩展的集成,但也限制了多线程在 CPU 密集型任务中的性能。对于 I/O
密集型任务,多线程仍然有效,因为 I/O 操作会释放 GIL。对于 CPU 密集型任务,可以考虑使用多进程、C 扩展或异步编程来绕过 GIL 的限制。
理解 GIL 的工作原理和限制,有助于开发者选择合适的并发模型,从而编写出更高效的 Python 程序。

浙公网安备 33010602011771号