Python 全局解释器锁给出的“保证”正在变化
Python 的全局解释器锁(GIL)正在被重新审视,这引出了一个问题:Python 实际上提供了哪些线程安全保证,而哪些只是 CPython 实现上的偶然结果。评论者争论依赖 GIL 获得原子操作到底是“有缺陷”的做法,还是在缺乏正式语言规范的生态中已经成为事实标准;同时也讨论核心开发者在推进可选无 GIL 构建时,究竟有多大义务不能“破坏用户空间”。许多人认为,按对象加锁并更清楚地文档化哪些操作真正原子,是一种必要但风险很高的转变:它会牺牲一些单线程性能和隐式安全性,但换来更好的多核利用率与更可预测的并发语义。
GIL 的范围与原子性保证
- 许多人认为,把“GIL 让事情看起来像原子操作”的行为当作真正的线程安全是一种有缺陷的设计;应该使用正确的同步原语。
- 也有人反驳说,当生态系统中的大多数部分都依赖某种行为时,它就会变成事实标准,因而很难改变(Hyrum 定律,“不要破坏用户空间”)。
- 说明:GIL 主要保护解释器/内部数据,而不是用户级别的竞态条件。像
x = self._next_id; self._next_id += 1这样的代码,在今天并不是行为上线程安全,只是内存安全。 - 一些 list/dict 操作被明确文档化为原子操作(例如
D[x] = y),人们也确实依赖这一点;在没有 GIL 的世界中保留这类保证被认为至关重要。
移除 GIL 的计划与担忧
- 未来的
--disable-gil/nogil 构建将使用细粒度的按对象锁;典型的 list/dict 修改不应导致段错误,而且通常仍会保持原子性。 - 给出的例子是:在 nogil 下并发调用
list.extend,结果要么是完整的 A-then-B,要么是 B-then-A,而不会交错。 - 开销方面:据说额外加锁会让单线程代码变慢大约 5–10%;有人担心“每次可变写入都加锁”会很“慢”,也有人认为未争用的锁很便宜。
- 人们担心哪些操作会保证原子、哪些不会,尤其是在涉及迭代器和用户代码的地方。预计会有更好的线程安全文档,但这仍然是“一大堆工作”。
Python 语义、规范与实现细节
- 关于 Python 是否有“规范”的争论很大:
- 一方认为:语言参考文档加上 PEP 构成了事实上的规范,区分了语言层面的保证与仅属于 CPython 的细节(例如 GIL、引用计数)。
- 另一方认为:这只是描述性的,而不是正式标准;真正的参考实现是 CPython 本身,而 GIL 在实践中就是 Python 语义的一部分。
- 其他实现(PyPy、Jython、IronPython、MicroPython 等)在各种方面都有偏离,这也强化了这样一个观点:某些 CPython 行为(如 GIL)不能被视为语言所必需的特性。
实际影响与使用场景
- 一些 Web 和 I/O 密集型开发者表示,从未被 GIL 限制过;线程主要只是用来隐藏 I/O 延迟。
- 而在 CPU 密集、数据处理和 ML 场景中,另一些人则把 GIL 视为主要障碍,只能转而使用 multiprocessing,并承担 pickle 的开销。
- 还有人建议保留 GIL 兼容模式,甚至把无 GIL 的模型升级为“Python 4”,这反映出语义与生态变化的规模之大。
关于版本号的插曲
- 还有一段关于 Python
3.9与3.13风格的讨论:版本组成部分是用点分隔的整数,而不是小数。 - 这被描述为一种常见的、类似 semver 的结构,不过 Python 的“次版本”仍然可能引入破坏性变更。