浅析Python的GIL和线程安全

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析Python的GIL和线程安全相关的知识,希望对你有一定的参考价值。

在这里我们将介绍Python的GIL和线程安全,希望大家能从中理解Python里的GIL,以及GIL的前世今生。

 

对于Python的GIL和线程安全很多人不是很了解,通过本文,希望能让大家对Python的GIL等内容有所帮助。本文还将就主要谈下笔者对线程安全的一些理解。

摘要

什么是线程安全? 为什么Python会使用GIL的机制?

在多核时代的到来的背景下,基于多线程来充分利用硬件的编程方法也不断发展起来, 但是一旦 牵扯到多线程,就必然会涉及到一个概念,即 线程安全, 本文就主要谈下笔者对线程安全的一些理解.

而Python为很多人所抱怨的一点就是GIL,那么Python为什么选择使用GIL, 本文也就这个问题进行一些讨论.

Contents

摘要 引入 线程安全 GIL 个人的观点 参考资料 本文的rst源码 . 引入

 

你的PC或者笔记本还是单核吗? 如果是,那你已经out了.

随着纳米技术的不断进步, 计算机芯片的工艺也在进步,但是已经很难在工艺上的改进来提高 运算速度而满足 摩尔定理, 所以intel, amd相继在采用横向的扩展即增加更多的CPU, 从而双核, 4核, N核不断推出,于是我们进入了多核时代.

于是一个问题出现了, 多核时代的出现对于我们程序员而言意味着什么, 我们如何利用多核的优势?

在回答这个问题之前,建议对 进程 和 线程 不熟悉的读者可以先补下相关的知识.

当然方案是,可以采用 多进程, 也可以采用 多线程. 二者的最大区别就是, 是否共享资源, 后者是共享资源的,而前者是独立的. 所以你也可能想起了google chrome为什么又开始使用独立的进程 来作为每个tab服务了(不共享数据,意味着有更好的安全性).

相对于进程的轻型特征,多线程环境有个最大的问题就是 如何保证资源竞争,死锁, 数据修改等.

于是,便有了 线程安全 (thread safety)的提出.

线程安全

Thread safety is a computer programming concept applicable in the context of multi-threaded programs.A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads.In particular, it must satisfy the need for multiple threads to access the same shared data,and the need for a shared piece of data to be accessed by only one thread at any given time.

上面是wikipedia中的解释, 换句话说, 线程安全 是在多线程的环境下, 线程安全能够保证多个线程同时执行时程序依旧运行正确, 而且要保证对于共享的数据,可以由多个线程存取,但是同一时刻只能有一个线程进行存取.

既然,多线程环境下必须存在资源的竞争,那么如何才能保证同一时刻只有一个线程对共享资源进行存取?

加锁, 对, 加锁可以保证存取操作的唯一性, 从而保证同一时刻只有一个线程对共享数据存取.

通常加锁也有2种不同的粒度的锁:

fine-grained(所谓的细粒度), 那么程序员需要自行地加,解锁来保证线程安全

coarse-grained(所谓的粗粒度), 那么语言层面本身维护着一个全局的锁机制,用来保证线程安全

前一种方式比较典型的是 java, Jython 等, 后一种方式比较典型的是 CPython (即Python).

前一种本文不进行讨论, 具体可参考 java 中的多线程编程部分.

至于Python中的全局锁机制,也即 GIL (Global Interpreter Lock), 下面主要进行一些讨论.

Python的GIL

 

什么是GIL ? 答案可参考wikipedia中的说明, 简单地说就是:

每一个interpreter进程,只能同时仅有一个线程来执行, 获得相关的锁, 存取相关的资源.

那么很容易就会发现,如果一个interpreter进程只能有一个线程来执行, 多线程的并发则成为不可能, 即使这几个线程之间不存在资源的竞争.

从理论上讲,我们要尽可能地使程序更加并行, 能够充分利用多核的功能, 那么Python为什么要使用 全局的GIL 来限制这种并行呢?

这个问题,其实已经得到了很多的讨论, 不止十年, 可以参考下面的文档:

反对GIL的声音:

An open letter to Guido van Rossum (这个文章值得一看,下面有很多的留言也值得一看)

认为GIL不能去除的:

It isn‘t Easy to Remove the GIL (这个文章来自python作者 Guido, 他说明了什么要使用 GIL)

其它的一些讨论很容易从Google来搜索得到, 譬如: GIL at google.

那么,简单总结下双方的观点.

认为应该去除GIL的:

不顺应计算机的发展潮流(多核时代已经到来, 而 GIL 会很影响多核的使用)

大幅度提升多线程程序的速度

认为不应该去除GIL 的(如果去掉,会):

写python的扩展(module)时会遇到锁的问题,程序员需要繁琐地加解锁来保证线程安全

会较大幅度地减低单线程程序的速度

后者是Guido最为关切的, 也是不去除GIL最重要的原因, 一个简单的尝试是在1999年(十年前), 最终的结果是导致单线程的程序速度下降了几乎2倍.

归根结底,其实就是多进程与多线程的选择问题, 有一段话比较有意思, 可以参考 http://www.artima.com/forums/flat.jsp?forum=106&thread=214235.

我引用如下:

I actually don‘t think removing the GIL is a good solution.

But I don‘t think threads are a good solution, either.

They‘re too hard to get right, and I say that after spending literally years studying threading in both C++ and Java.

Brian Goetz has taken to saying that no one can get threading right.

引自Bruce Eckel 对Guido 的回复. 而Bruce Eckel 是何许人, 如果你了解Java 或者C++, 那么应该不会不知道他.

个人的观点

 

那么,从我自己的角度来看(我没有太多的多线程编程经验), 先不论多线程的速度优势等,我更加喜欢多进程的是:

简单,无需要人为(或者语言级别)的加解锁. 想想 java 中的多线程编程,程序员通常会在此处出错(Java程序员可以思考下)

安全, 这也是浏览器为什么开始使用多进程的一个原因

依照Python自身的哲学, 简单 是一个很重要的原则,所以, 使用 GIL 也是很好理解的.

当然你真的需要充分利用多核的速度优势,此时python可能并非你最佳的选择,请考虑别的语言吧,如Java, erlang 等.

原文标题:线程安全及Python中的GIL

链接:http://www.cnblogs.com/mindsbook/archive/2009/10/15/thread-safety-and-GIL.html

以上是关于浅析Python的GIL和线程安全的主要内容,如果未能解决你的问题,请参考以下文章

多线程——GIL信号量递归锁

python GIL锁进程池与线程池同步异步

GIL线程全局锁 协程

python 线程(创建2种方式,守护进程,锁,死锁,递归锁,GIL锁,其他方式)

GIL锁,线程池,同步异步

GIL 线程池 进程池 同步 异步