一个不解之谜

  一段代码

def CountDown(n):
while n > 0:
n -= 1 # CountDown(100000000)
#==8秒 from threading import Thread n = 100000000 t1 = Thread(target=CountDown, args=[n // 2])
t2 = Thread(target=CountDown, args=[n // 2])
t1.start()
t2.start()
t1.join()
t2.join()
#==9s
  可以看出,多线程并没有让上面代码变得更快,这是Python的问题?
  Python的线程,的的确确封装了底层操作系统线程。在Linux系统里是Pthread(全称为POSIXThread),而在Windows系统里是WindowsThread。

为什么有GIL

  GIL,是最流行的 Python 解释器 CPython 中的一个技术术语。它的意思是全局解释器锁,本质上是类似操作系统的 Mutex。每一个 Python 线程,在 CPython 解释器中执行时,都会先锁住自己的线程,阻止别的线程执行。CPython 会做一些小把戏,轮流执行 Python 线程。

  所以说,CPython 引进 GIL 其实主要就是这么两个原因:
  一是设计者为了规避类似于内存管理这样的复杂的竞争风险问题(race condition);
  二是因为 CPython 大量使用 C 语言库,但大部分 C 语言库都不是原生线程安全的(线程安全会降低性能和增加复杂度)。

GIL 是如何工作的?

  如图,Thread 1、2、3 轮流执行,每一个线程在开始执行时,都会锁住 GIL,以阻止别的线程执行;同样的,每一个线程执行完一段后,会释放 GIL,以允许别的线程开始利用资源。

  

  

  CPython还有check interval机制。早期的 Python 是 100 个 ticks,大致对应了 1000 个 bytecodes;而 Python 3 以后,interval 是 15 毫秒。
for (;;) {
if (--ticker < 0) {
ticker = check_interval; /* Give another thread a chance */
PyThread_release_lock(interpreter_lock); /* Other threads may run now */ PyThread_acquire_lock(interpreter_lock, 1);
} bytecode = *next_instr++;
switch (bytecode) {
/* execute the next instruction ... */
}
}
  从这段代码中,我们可以看到,每个 Python 线程都会先检查 ticker 计数。只有在 ticker 大于 0 的情况下,线程才会去执行自己的 bytecode。

Python 的线程安全

  GIL 的设计,主要是为了方便 CPython 解释器层面的编写者,而不是 Python 应用层面的程序员。

  作为 Python 的使用者,我们还是需要 lock 等工具,来确保线程安全。比如下面的这个例子:

n = 0
lock = threading.Lock() def foo():
global n
with lock:
n += 1

如何绕过 GIL?

  事实上,很多高性能应用场景都已经有大量的 C 实现的 Python 库,例如 NumPy 的矩阵运算,就都是通过 C 来实现的,并不受 GIL 影响。 所以,大部分应用情况下,你并不需要过多考虑 GIL。因为如果多线程计算成为性能瓶颈,往往已经有 Python 库来解决这个问题了。

  总的来说,绕过 GIL 的大致思路有这么两种就够了:
  1. 绕过 CPython,使用 JPython(Java 实现的 Python 解释器)等别的实现;
  2. 把关键性能代码,放到别的语言(一般是 C++)中实现。

参考

  极客时间《Python核心技术实战》专栏

Python进阶:GIL(全局解释器锁)的更多相关文章

  1. [Python 多线程] GIL全局解释器锁 (十三)

    Queue 标准库queue模块,提供FIFO(先进先出)的Queue.LIFO(后进先出)的队列.优先队列. Queue类是线程安全的,适用于多线程间安全的交换数据.内部使用了Lock和Condit ...

  2. Python 36 GIL全局解释器锁 、vs自定义互斥锁

    一:GIL全局解释器锁介绍 在CPython中,全局解释器锁(或GIL)是一个互斥锁, 它阻止多个本机线程同时执行Python字节码.译文:之所以需要这个锁, 主要是因为CPython的内存管理不是线 ...

  3. python基础--GIL全局解释器锁、Event事件、信号量、死锁、递归锁

    ps:python解释器有很多种,最常见的就是C python解释器 GIL全局解释器锁: GIL本质上是一把互斥锁:将并发变成串行,牺牲效率保证了数据的安全 用来阻止同一个进程下的多个线程的同时执行 ...

  4. 关于python的GIL全局解释器锁的简单理解

    GIL是解释器内部的一把锁,确切一点说是CPython解释器内部的一把锁,所以要注意区分 这和我们在Python代码中使用线程锁Lock并不是一个层面的概念. 1. GIL产生的背景: 在CPytho ...

  5. Python 之 GIL 全局解释器锁

    GIL(全局解释器锁) GIL锁即全局解释器锁,是 CPython 解释器的特性.它的作用是保证了同一时刻只有一个线程执行 Python 字节码. 它并不是 Python 的特性,它的存在是 CPyt ...

  6. Python之路-python(paramiko,进程和线程的区别,GIL全局解释器锁,线程)

    一.paramiko 二.进程.与线程区别 三.python GIL全局解释器锁 四.线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生 ...

  7. Python自动化 【第九篇】:Python基础-线程、进程及python GIL全局解释器锁

    本节内容: 进程与线程区别 线程 a)  语法 b)  join c)  线程锁之Lock\Rlock\信号量 d)  将线程变为守护进程 e)  Event事件 f)   queue队列 g)  生 ...

  8. 网络编程-Python高级语法-GIL全局解释器锁

    知识点:GIL全局解释器锁其实和Python没有任何关系,是由于当初编写Python解释器时留下的,它只对多线程有影响,GIL保证同一时刻只有一个线程在运行,即使是多核配置电脑,同一时刻也只会让一个线 ...

  9. Python中对于GIL全局解释器锁的一点理解

    GIL全局解释器锁 python最初开发时,开发人只考虑到了单核CPU的,为解决多线程运算之间的数据完整性和状态同步选择了加锁的方式.即GIL锁. 而目前的CPU都有多个核心,在运行python的某个 ...

  10. python GIL全局解释器锁,多线程多进程效率比较,进程池,协程,TCP服务端实现协程

    GIL全局解释器锁 ''' python解释器: - Cpython C语言 - Jpython java ... 1.GIL: 全局解释器锁 - 翻译: 在同一个进程下开启的多线程,同一时刻只能有一 ...

随机推荐

  1. 【loj3119】【CTS2019】随机立方体

    题目 ​ 一个 $ n m l $ 的立方体等概率填入 $ 1-nml $ ; ​ 定义一个位置是极大的当且仅当这个位置比三位坐标的至少一维与之相等的位置的值都大. ​ 询问极大值恰好有\(k\)个的 ...

  2. 《RabbitMQ 实战》读书笔记

    MQ的好处: 1.业务上接口(系统扩展性变强) 2.性能提升(同步变异步,效率提高,还方便做负载均衡) 3.技术兼容(可以连接各种不同语言的系统,作为粘合剂) 读书笔记: 1.消息队列的应用场景:系统 ...

  3. 【洛谷P5596】【XR-4】题

    solution \(y^2-x^2=ax+b\) \(y^2=x^2+ax+b\) 当\(x^2+ax+b\)为完全平方式时\(Ans=inf\) \(x \leq y\) 不妨令 \(y=x+t\ ...

  4. C++通过迭代修改字符串本身(auto类型说明符)

    以字符串这种支持 for (declaration : expression) statement 这样for语句迭代的数据结构为例,我们看看auto关键字在类型推断中的作用. string s = ...

  5. C Primer Plus--C预处理器和C库(2)

    目录 #include指令 头文件 其他指令 #undef 条件编译 内联函数 #include指令 #include <头文件.h>//在标准系统目录中寻找头文件 #include &q ...

  6. 从浏览器输入url到显示页面的过程 (前端面试题)

    域名DNS解析,解析到真正的IP地址             | 客户端与服务端建立TCP连接,3次握手 | 客户端发送Http请求 | server接收到http请求,处理,并返回 | 客户端接收到 ...

  7. Spring AOP的实现记录操作日志

    适用场景: 记录接口方法的执行情况,记录相关状态到日志中. 注解类:LogTag.java package com.lichmama.spring.annotation; import java.la ...

  8. 帝国cms万能标签实现标题截取后自动加入省略号的方法

    很多采用帝国CMS建站的站长都会遇到标题过长导致页面排版错乱的情况,这时候往往需要用标题截取并追加上省略号的方法予以解决.对此,帝国CMS万能标签标题截取后自动加入省略号,没有达到字数的则不加省略号可 ...

  9. Mac复制粘贴文本时默认使用无格式模式

    参考:How to Paste Everything as Plain Text 写文章的时候,用的最多的就是copy和paste了,可是现在Mac和Win默认都是会连格式一起复制,真是逆天,导致每次 ...

  10. git - 3.分支

    分支介绍 每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支.截止到目前,只有一条时间线, 在Git里,这个分支叫主分支,即master分支. HEAD严格来说不是指向提交,而是指向mas ...