穿透 Python GIL 的迷雾:它到底锁的是什么?为什么 CPython 仍坚持多线程?

Python底层原理实战:运行时行为、内存布局与GIL深度解析 Python作为高级语言,其表面简洁性常掩盖了底层运行时机制的复杂性。理解CPython对象模型、引用计数与内存布局,是诊断OOM、循环引用和性能抖动的关键基础;掌握GIL的实际调度逻辑与规避策略,则直接决定多线程/多进程场景下的真实吞吐能力。这些原理并非理论空谈——它们映射到asyncio事件循环饥饿、weakref失效、__repr__递归崩溃、描述符属性拦截异常等高频工程问题。本文聚焦Python运行时行为与对象生命周期两大核心维度,通过可调试代码、perf观测、源码级剖析及生产级重构案例,将抽象概念 阅读详情

穿透 Python GIL 的迷雾:它到底锁的是什么?为什么 CPython 仍坚持多线程?

在我这些年教授 Python、参与高性能系统开发的经历中,最常被问到的问题之一就是:

“GIL 到底锁的是什么?是不是锁住了所有线程?”

“既然 GIL 会阻塞多线程并行,那 CPython 为什么还要保留多线程?”

“多线程在 Python 中还有意义吗?”

这些问题背后,是 Python 开发者对性能、并发和底层机制的深深困惑。GIL(Global Interpreter Lock,全局解释器锁)几乎是 Python 世界里最具争议的设计之一,它既是历史遗留,又是现实妥协;既是性能瓶颈,又是稳定基石。

这篇文章,我将带你从 Python 的发展背景讲起,逐步深入 GIL 的本质、触发机制、设计原因与工程实践价值,并结合大量代码示例与真实案例,帮助你真正理解:

✅ GIL 到底锁住了什么
✅ 为什么 CPython 仍然保留多线程
✅ 多线程在 Python 中的正确使用方式
✅ 如何绕过 GIL 获得真正的并行

让我们从头开始。


一、开篇:Python 的发展与 GIL 的诞生

Python 自 1991 年诞生以来,以“简洁、优雅、可读性强”著称。它的设计哲学之一是:

让开发者专注于业务逻辑,而不是内存管理与线程安全。

在 Python 的早期版本中,内存管理依赖引用计数(Reference Counting)。引用计数的更新不是原子操作,因此在多线程环境下会产生竞争条件(Race Condition)。

为了避免复杂的锁设计、提升解释器稳定性、降低开发成本,Python 的作者 Guido van Rossum 做了一个关键决定:

用一个全局锁保护解释器内部状态。

这就是 GIL 的起源。


二、GIL 到底锁的是什么?(核心问题)

很多人以为 GIL 锁住的是:

  • 所有线程
  • 所有 Python 对象
  • 所有 CPU 核心

但这都是误解。

✅ GIL 锁住的不是 Python 对象,而是整个解释器状态(Interpreter State)

更准确地说:

GIL 锁住的是 CPython 的字节码执行器(Bytecode Interpreter)。

也就是说:

  • 只有持有 GIL 的线程才能执行 Python 字节码
  • 其他线程即使被调度,也无法执行 Python 层面的运算
  • GIL 不锁 I/O,不锁 C 扩展,不锁系统调用

✅ GIL 的本质:保护引用计数与对象模型

CPython 的对象模型不是线程安全的:

  • 引用计数不是原子操作
  • 内存分配器不是线程安全的
  • 对象内部状态不是线程安全的

因此:

GIL 是 CPython 为了保证对象模型一致性而加的全局互斥锁。


三、GIL 的工作机制:什么时候会释放?什么时候会阻塞?

GIL 的行为可以总结为:

一个线程持有 GIL → 执行 Python 字节码
其他线程等待 → 无法执行 Python 字节码

但 GIL 并不是一直不放,它会在以下情况释放:


✅ 1. I/O 操作时释放 GIL(最重要)

例如:

import threading
import time

def task():
    time.sleep(1)  # I/O 操作,释放 GIL
    print("done")

for _ in range(10):
    threading.Thread(target=task).start()

time.sleep() 会释放 GIL,因此多个线程可以并发执行。

这也是为什么:

✅ Python 多线程适合 I/O 密集型任务
❌ 不适合 CPU 密集型任务


✅ 2. C 扩展主动释放 GIL

例如 NumPy:

import numpy as np

def compute():
    a = np.random.rand(10000, 10000)
    b = a @ a  # C 扩展内部释放 GIL

NumPy 的矩阵运算在 C 层面执行,不受 GIL 限制,可以真正并行。


✅ 3. 线程切换时释放 GIL(每隔一定字节码执行次数)

Python 3.2 之前:

  • 每执行 100 条字节码 切换一次线程

Python 3.2 之后:

  • 改为基于时间片(默认 5ms)

四、为什么 CPython 仍然保留多线程?

这是很多人最困惑的问题:

“既然 GIL 限制了并行,那 Python 为什么还要多线程?”

答案有三个层面。


✅ 1. 多线程对 I/O 密集型任务非常有效

例如:

  • 网络请求
  • 文件读写
  • 数据库访问
  • 爬虫
  • 日志处理

这些操作会释放 GIL,因此多线程可以真正并发。

示例:

import requests
import threading

def fetch(url):
    print(requests.get(url).status_code)

urls = ["https://example.com"] * 20

for u in urls:
    threading.Thread(target=fetch, args=(u,)).start()

多线程可以显著提升吞吐量。


✅ 2. 多线程比多进程更轻量

  • 线程共享内存
  • 创建成本低
  • 上下文切换快
  • 适合高并发 I/O

在 Web 服务中,多线程仍然是主流模型之一。


✅ 3. CPython 的生态大量依赖线程

例如:

  • logging 模块内部使用线程
  • asyncio 的事件循环使用线程池
  • concurrent.futures.ThreadPoolExecutor
  • 各种 C 扩展内部使用线程

如果移除线程,整个生态将崩溃。


五、GIL 的误区:你以为它限制了所有并行,其实没有

以下情况不受 GIL 限制:


✅ 1. I/O 操作(会释放 GIL)

例如:

  • 网络请求
  • 文件读写
  • sleep
  • socket
  • subprocess

✅ 2. C 扩展(主动释放 GIL)

例如:

  • NumPy
  • Pandas
  • TensorFlow
  • PyTorch
  • Pillow

这些库内部使用 C/C++,可以真正多核并行。


✅ 3. 多进程(完全绕过 GIL)

from multiprocessing import Pool

def f(x):
    return x * x

with Pool(4) as p:
    print(p.map(f, range(10)))

每个进程有自己的 GIL,因此可以真正并行。


六、实战:如何绕过 GIL 获得真正的并行?

下面给出工程实践中最常用的三种方式。


✅ 方式 1:使用多进程(multiprocessing)

适合 CPU 密集型任务:

from multiprocessing import Pool

def cpu_task(n):
    return sum(range(n))

with Pool() as p:
    print(p.map(cpu_task, [10_000_000] * 4))

✅ 方式 2:使用 C 扩展(NumPy、Cython、PyPy)

例如 NumPy:

import numpy as np

a = np.random.rand(20000, 20000)
b = a @ a  # 真正并行

✅ 方式 3:使用 asyncio(适合 I/O)

import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as resp:
        return await resp.text()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, "https://example.com") for _ in range(20)]
        await asyncio.gather(*tasks)

asyncio.run(main())

七、GIL 的未来:会被移除吗?

Python 社区一直在探索移除 GIL 的可能性。

✅ PEP 703:可选无 GIL 的 CPython(Huge Progress)

2023 年,Sam Gross 提出:

让 CPython 支持无 GIL 模式(可选)

目前已经合并到 Python 3.13 的实验性分支。

未来 Python 可能会:

  • 默认保留 GIL(兼容性)
  • 提供无 GIL 模式(高性能)

这是 Python 历史上最重要的变革之一。


八、总结:GIL 并不是 Python 的“罪人”,而是历史的妥协

本文从多个角度深入解析了 GIL:

✅ GIL 锁住的是解释器状态,而不是 Python 对象
✅ GIL 的本质是保护引用计数与对象模型
✅ 多线程仍然适用于 I/O 密集型任务
✅ C 扩展与多进程可以绕过 GIL
✅ Python 未来可能支持无 GIL 模式

GIL 是 Python 的“保护伞”,也是“枷锁”。理解它,你才能真正写出高性能的 Python 程序。


九、互动时间

我很想听听你的经验:

  • 你在 Python 多线程中遇到过哪些坑
  • 你认为 GIL 是 Python 的优势还是劣势
  • 你希望未来的 Python 如何处理并发

欢迎留言交流,我们一起探索 Python 的更多可能性。

Pyhton中GIL的工作原理及性能优化问题 一、 GIL的说明 首先说明,GIL(global interpreter lock),中文称全局解释器锁,并不是Python的特性,它是CPython的产物,像JPython和IronPython因为其语言的特性,所以不需要GIL的帮助。 GIL的工作原理及导致的问题 为了解决多线程之间数据完整性和状态同步的问题,最简单方法自然就是加锁,这是GIL的由来。 在一个古老的单核CPU上调度多个线程任务,大家相互共享一个全局锁,谁在CPU执行,谁就占有这把锁,直到这个线程因为IO操作或者Timer Ti 阅读详情

相关推荐

horizon之为connection服务器配置证书

打开connection1服务器(connection2 同connection1)再输入connection1的IP地址 可通过域名和IP地址进行登录。这时候用域名登录就发现证书已经没有警告了。win+R 调出“运行”,输入mmc。connection2 同上。如果还有报错请重启一下服务器。打开connection1。可以把这个先删掉 没用。打开DC_01服务器。右键选择证书颁发机构。友好名称只能是vdm。重启之后就没有报错了。

送个祝福给小豪的博客 2960

python——GIL锁详解

对于IO密集型应用,即便有GIL存在,由于IO操作会导致GIL释放,其他线程能够获得执行权限。由于多线程的通讯成本低于多进程,因此偏向使用多线程。对于计算密集型应用,由于CPU一直处于被占用状态,GIL锁直到规定时间才会释放,然后才会切换状态,导致多线程处于绝对的劣势,此时可以采用多进程+协程。

python全栈 8479

关于示波器测485串口波特率的使用方法总结

之前没有用过示波器,更不知道怎么来测试串口的波特率,结果遇到一客户说我们产品的波特率达不到9600,只有9100,为了验证这一说法,我们只能自己测试一下。说明:产品通过485接电脑,用串口调试助手接收都是没有问题的,用客户的设备接收就有问题。 下面说一下示波器测波特率的方法步骤: 显示高低跳变的情况: 1.先把示波器表笔的地线(夹子)夹485B,表笔夹485A。 2.往串口发送数据:如发送

lianxue1986的专栏 3万+

python中的线程锁的了解与学习

全局解释器锁(Global Interpreter Lock,简称GIL)是CPython解释器中一个重要的机制,其存在主要是为了确保线程安全和简化内存管理。它限制了单个进程在同一时间只能执行其中一个线程的代码。GIL 是 CPython解释器为了简化内存管理、保证线程安全而引入的设计决策。它在简化开发和保证程序稳定性方面发挥了重要作用,但同时也限制了 Python在多核处理器上的并行执行能力,特别是在 CPU 密集型任务中。

weixin_43574373的博客 2022

Visual Tracker Benchamark傻瓜式教程(二)

问题  1 1  在调用main.m跑TRE和SRE时,因为会生成相应的.mat数据文件,如果跑完以后发现自己算法生成的.mat文件有问题,那么要把该文件删除,待找到错误原因后,重新生成相应的.mat文件。因为程序中写的是如果已经存在了.mat文件,例如已经存在car4_STC.mat文件,即使该文件中的数据有错误,但是重新跑时会默认该文件中的数据写完,不会重新写入数据 问题  2  b

mingbujingren的专栏 9928

ACM算法模板 · 一些常用的算法模板-模板合集(打比赛专用)

萌新常用的都在这了,本萌新的模板集。

张天枭の博客 5万+

【python线程与GIL 锁】

消息队列 队列:先进先出 堆栈:先进后出 from multiprocessing import Queue q = Queue(5) # 自定义队列的长度 # 朝队列中存放数据 q.put(111) q.put(222) q.put(333) print(q.full()) # False 判断队列是否满了 q.put(444) q.put(555) print(q.full()) # True # q.put(666) # 超出最大长度 原地阻塞等待队列中出现空位 print(q.get(

Ban_a的博客 1480

流媒体弱网优化之路(NACK)——纯NACK方案的优化探索

流媒体学习之路(NACK)——纯NACK方案的最高性能 文章目录流媒体学习之路(NACK)——纯NACK方案的最高性能一、NACK简介二、Nack的理解2.1 一种补救策略2.2 什么时候要?2.3 要多少?2.4 怎么要?三、计算分析3.1 rtt强相关3.2 补包次数计算3.3 Nack的定时发送3.4 nack重传限制四、实验   近期在针对纯丢包场景做一些简单的探索实验,一个让我困扰的问题就是:不使用FEC而只是通过纯NACK进行补偿,到底能在实验环境下抵抗多少的丢包呢?每一次丢包补偿的最大时长

qw225967的博客 1万+

[python]线程GIL锁

#gil global interpreter lock (cpython) #python中一个线程对应于c语言中的一个线程 #gil使得同一个时刻只有一个线程在一个cpu上执行字节码, 无法将多个线程映射到多个cpu上执行 #gil会根据执行的字节码行数以及时间片释放gil,gil在遇到io的操作时候主动释放 # import dis # def add(a): # a = a+1 ...

qq_38065133的博客 231

SAP 费用化采购订单简介

费用性采购的核心特点费用性采购的流程费用性采购的优势适用场景业务场景前台操作1、创建成本中心---KS012、创建采购订单---ME21N3、采购订单审批---ME29N4、采购订单收货---MIGO5、查看成本中心费用---S_ALR_870136116、控制费用化的采购订单自动带出科目和成本中心SAP费用性采购(Expense Procurement)指的是企业在采购过程中,直接将采购的物料或服务成本计入费用科目,而不是记入库存账户的采购行为。

我小时候很黑的博客 2016

Python 爬虫实战:爬取哔哩哔哩知识区,抓取高赞课程信息

哔哩哔哩(Bilibili)是一个广受欢迎的综合性视频社区,其中的知识区汇聚了大量优质的教育、科普、技术等课程内容。本文将指导读者如何使用 Python 爬虫技术从哔哩哔哩知识区抓取高赞课程信息,并进行数据分析。本文深入探究了利用 Python 爬虫技术从哔哩哔哩知识区抓取高赞课程信息的全流程。从前期的环境搭建、模拟登录,到核心的课程数据抓取、存储,再到后续的数据分析与可视化,都详细展开讲解并提供代码支持。通过此方法所获取的数据,能够助力创作者优化内容、用户发现优质课程、平台洞察用户需求。

u014481728的博客 1137

【python基础】python GIL(全局解释器锁) 和 多线程锁

文章目录什么是 GIL?有了 GIL 还需要线程锁吗?参考资料1 :[终于有人把GIL全局解释器说清楚了](https://zhuanlan.zhihu.com/p/311877485)2:[浅谈Python多线程之GIL描述](https://blog.youkuaiyun.com/qq_34359754/article/details/115209158)3:[多线程锁机制](https://www.cnblogs.com/lidagen/p/7237674.html) 什么是 GIL? Python 虚拟机默认使

shengnan970116的博客 1411

SonarQube 完整手册(2026 最新)

摘要: SonarQube是一款静态代码分析(SAST)平台,支持20+语言(如Java/Go/Python),提供Bug、漏洞、代码异味等检测,集成DevOps流水线。其架构包含服务端、扫描器和SonarLint插件。2026年四大版本:免费社区版(基础功能)、开发者版(多分支/PR分析)、企业版(合规治理)、数据中心版(高可用集群)。核心功能包括质量门禁(QualityGate)、密钥泄露检测及六大质量指标。部署推荐Docker+PostgreSQL,扫描支持Maven/CLI方式。

SmallBull的博客 670

GIL线程全局锁

1.GIL概述 GIL ( Global Interperter Lock ) 称作全局解释器锁。首先需要明确一点,我们所讲的 GIL 并不是 Python 语言的特性,它是在实现 Python 解释器时引用的一个概念。GIL 只在CPython 解释器上存在。 2.GIL优缺点 优点: GIL 是 Python 解释层面的锁,即Python为了保证线程安全而采取的独立线程运行的限制,说白了就...

月夜寻花香的博客 617

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

既然有GIL了那么为什么在python中多线程编码时候还需要加锁?

GIL 由于 python 解释器(Cpython interpreter)不是线程安全(thread-safe)的,所以 Cpython interpreter 的实现中使用了GIL(global interpreter lock)来阻止多线程同时在一个 pyobject 上操作。这里所说的 “不是线程安全“ 是指Cpython interpreter在内存管理上不是线程安全的。比如,两个线程同...

bdss58的专栏 5402

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

GIL 全局锁

#1 python的解释器有很多,cpython,jpython,pypy(python写的解释器) #2 python的库多,库都是基于cpython写起来的,其他解释器没有那么多的库 #3 cpython中有一个全局大锁,每条线程要执行,必须获取到这个锁 #4 为什么会有这个锁呢?因为垃圾回收线程不是线程安全的,所有线程必须拿到这把锁,才能执行。 #5 python的多线程其实就是单线程 #6 某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只

Pinkman2k的博客 243

Python多进程 多线程 GIL锁

找出 GIL 究竟是什么,为什么它存在于 Python 中,它又是怎么影响多线程程序的 Python为了利用多核,Python开始支持多线程。而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁 于是有了GIL这把超级大锁 一个线程运行 Python ,而其他 N 个睡眠或者等待 I/O.”(即保证同一时刻只有一个线程对共享资源进行存取) Python 线程也可以等待threading....

DzzzzzZzzzz的博客 512

py并发编程:GIL锁、进程、线程、协程

1、进程、线程概念引入 1.1 进程的由来 进程就是一个程序在一个数据集上的一次动态执行过程。是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 进程一般由程序、数据集、进程控制块(PCB)三部分组成。 我们编写的程序用来描述进程要完成哪些功能以及如何完成; 数据集则是程序在执行过程中所需要使用的资源; 进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。 这里需要注意的是程序和进程的区别。一个程序是一个可执行的文件,

Sunny_Future的博客 1332

Day 36 GIL锁线程池信号量Event事件线程间通信

一、验证GIL锁的存在 ​ 我们如何验证GIL锁呢?我们知道因为有GIL锁所以我们所谓的多线程实际上只是一个线程,即使我们开很多个线程,其实也只是利用到了CPU的一个核心,而进程就不一样了,一个进程拥有一个GIL所以,我们开几个进程实际就是利用几个CPU from threading import Thread from multiprocessing import Process def run(): while True: pass if __name__ == '__

A1L__的博客 297

python GIL和线程锁

python GIL 在多线程环境中,Python虚拟机按照以下方式执行。 1.设置GIL。 2.切换到一个线程去执行。 3.运行。 4.把线程设置为睡眠状态。 5.解锁GIL。 6.再次重复以上步骤。 线程锁 用theading.Lock()创建一个lock的实例。 在线程启动之前通过lock.acquire()加加锁,在线程结束之后通过lock.release()释放锁。 这层锁是用户开的锁,就是我们用户程序的锁。跟我们这个GIL没有关系,但是它把这个数据相当于copy了两份,所以在这里加锁,以确保

sinat_28375239的博客 470

Python多线程和多进程(一) GIL锁和使用Thread创建多线程

本系列文章目录 展开/收起 Python多线程和多进程(一) GIL锁和使用Thread创建多线程 Python多线程和多进程(二) 线程同步之互斥锁和重入锁 Python多线程和多进程(三) 线程同步之条件变量 Python多线程和多进程(四) 线程同步之信号量 Python多线程和多进程(五) 多线程管理——...

jqsfjqsf的博客 520

python中gil锁和线程锁_Python线程——GIL锁、线程锁(互斥锁)、递归锁(RLock)...

GIL锁​ 计算机有4核,代表着同一时间,可以干4个任务。如果单核cpu的话,我启动10个线程,我看上去也是并发的,因为是执行了上下文的切换,让看上去是并发的。但是单核永远肯定时串行的,它肯定是串行的,cpu真正执行的时候,因为一会执行1,一会执行2.。。。。正常的线程就是这个样子的。但是,在python中,无论有多少核,永远都是假象。无论是4核,8核,还是16核…….不好意思,同一时间执行...

weixin_39746282的博客 401

py线程详解线程锁GIL

http://lzxandcyx.cn/2020/08/03/py%E7%BA%BF%E7%A8%8B%E8%AF%A6%E8%A7%A3%E7%BA%BF%E7%A8%8B%E9%94%81GIL/ 原文链接:python多线程详解(超详细) ​ python多线程详解 ​ 什么是线程? 线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位

qq_45841239的博客 311

python面试不得不知道的点——GIL

在进行GIL讲解之前,我们可以先回顾一下并行和并发的区别:并行:多个CPU同时执行多个任务,就好像有两个程序,这两个程序是真的在两个不同的CPU内同时被执行。并发:CPU交替处理多个任务,还是有两个程序,但是只有一个CPU,会交替处理这两个程序,而不是同时执行,只不过因为CPU执行的速度过快,而会使得人们感到是在“同时”执行,执行的先后取决于各个程序对于时间片资源的争夺。大家可以参考下图的实物图就...

python小白的逆袭之路 2万+

Python : GIL线程全局锁

线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。 解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能). ...

weixin_44523387的博客 266

python gil 解除_关于GIL(全局解释器锁)与线程互斥锁的理解

该楼层疑似违规已被系统折叠隐藏此楼查看此楼全局解释器锁简称:GIL(Global Interperter Lock)。首先需要明确一点就是,GIL并不是Python语言的特性,它是在现实Python解释器时引用的一个概念。GIL只在CPython解释器上存在。作用是保证同一时间内只有一个线程在执行。线程互斥锁和GIL的区别1.线程互斥锁是Python代码层面的锁,解决Python程序中多线程共享...

weixin_39911475的博客 325
上一篇: 深入 Python 内存世界:引用计数、标记-清除与分代回收的全景解析与实战指南
下一篇: 《Python == 与 is 的真相:从基础语义到底层机制,一篇让新手顿悟、老手沉默的深度解析》
铭渊老黄
铭渊老黄 新星创作者: Python技术领域 新星创作者: Python技术领域
博客等级 码龄7年 1万+粉丝 2220原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铭渊老黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值