首页 > 编程语言 >深入理解 python 虚拟机:GIL 源码分析——天使还是魔鬼?

深入理解 python 虚拟机:GIL 源码分析——天使还是魔鬼?

时间:2023-10-14 21:56:34浏览次数:47  
标签:python lock 虚拟机 源码 线程 pthread GIL data

深入理解 python 虚拟机:GIL 源码分析——天使还是魔鬼?

在目前的 CPython 当中一直有一个臭名昭著的问题就是 GIL (Global Interpreter Lock ),就是全局解释器锁,他限制了 Python 在多核架构当中的性能,在本篇文章当中我们将详细分析一下 GIL 的利弊和 GIL 的 C 的源代码。

选择 GIL 的原因

GIL 对 Python 代码的影响

简单来说,Python 全局解释器锁或 GIL 是一个互斥锁,只允许一个线程保持 Python 解释器的控制权,也就是说在同一个时刻只能够有一个线程执行 Python 代码,如果整个程序是单线程的话,这也无伤大雅,但是如果你的程序是多线程计算密集型的程序的话,这对程序的影响就很大了。

因为整个虚拟机都有一把大锁进行保护,所以虚拟的代码就可以认为是单线程执行的,因此不需要做线程安全的防护,直接按照单线程的逻辑就行了。不仅仅是虚拟机,Python 层面的代码也是这样,对于有些 Python 层面的多线程代码也可以不用锁保护,因为本身就是线程安全的:

import threading

data = []


def add_data(n):
	for i in range(n):
		data.append(i)


if __name__ == '__main__':
	ts = [threading.Thread(target=add_data, args=(10,)) for _ in range(10)]
	for t in ts:
		t.start()
	for t in ts:
		t.join()

	print(data)
	print(len(data))
	print(sum(data))

在上面的代码当中,当程序执行完之后 len(data) 的值永远都是 100,sum(data) 的值永远都是 450,因为上面的代码是线程安全的,可能你会有所疑惑,上面的代码启动了 10 个线程同时往列表当中增加数据,如果两个线程同时增加数据的时候就有可能存在线程之间覆盖的情况,最终的 len(data) 的长度应该小于 100 ?

上面的代码之所以是线程安全的原因是因为 data.append(i) 执行 append 只需要虚拟机的一条字节码,而在前面介绍 GIL 时候已经谈到了,每个时刻只能够有一个线程在执行虚拟机的字节码,这就保证了每个 append 的操作都是原子的,因为只有一个 append 操作执行完成之后其他的线程才能够执行 append 操作。

我们来看一下上面程序的字节码:

  5           0 LOAD_GLOBAL              0 (range)
              2 LOAD_FAST                0 (n)
              4 CALL_FUNCTION            1
              6 GET_ITER
        >>    8 FOR_ITER                14 (to 24)
             10 STORE_FAST               1 (i)

  6          12 LOAD_GLOBAL              1 (data)
             14 LOAD_METHOD              2 (append)
             16 LOAD_FAST                1 (i)
             18 CALL_METHOD              1
             20 POP_TOP
             22 JUMP_ABSOLUTE            8
        >>   24 LOAD_CONST               0 (None)
             26 RETURN_VALUE

在上面的字节码当中 data.append(i) 对应的字节码为 (14, 16, 18) 这三条字节码,而 (14, 16) 是不会产生数据竞争的问题的,因为他只是加载对象的方法和局部变量 i 的值,让 append 执行的方法是字节码 CALL_METHOD,而同一个时刻只能够有一个字节码在执行,因此这条字节码也是线程安全的,所以才会有上面的代码是线程安全的情况出现。

我们再来看一个非线程安全的例子:

import threading
data = 0
def add_data(n):
	global data
	for i in range(n):
		data += 1

if __name__ == '__main__':
	ts = [threading.Thread(target=add_data, args=(100000,)) for _ in range(20)]
	for t in ts:
		t.start()
	for t in ts:
		t.join()
	print(data)

在上面的代码当中对于 data += 1 这个操作就是非线程安全的,因为这行代码汇编编译成 3 条字节码:

  9          12 LOAD_GLOBAL              1 (data)
             14 LOAD_CONST               1 (1)
             16 INPLACE_ADD

首先 LOAD_GLOBAL,加载 data 数据,LOAD_CONST 加载常量 1,最后执行 INPLACE_ADD 进行加法操作,这就可能出现线程1执行完 LOAD_GLOBAL 之后,线程 2 连续执行 3 条字节码,那么这个时候 data 的值已经发生变化了,而线程 1 拿的还是旧的数据,因此最终执行的之后会出现线程不安全的情况。(实际上虚拟机在执行的过程当中,发生数据竞争比这个复杂很多,这里只是简单说明一下)

GIL 对于虚拟机的影响

除了上面 GIL 对于 Python 代码层面的影响,GIL 对于虚拟机来说还有一个非常好的作用就是他不会让虚拟机产生死锁的现象,因为整个虚拟机只有一把锁

标签:python,lock,虚拟机,源码,线程,pthread,GIL,data
From: https://www.cnblogs.com/Chang-LeHung/p/17764800.html

相关文章

  • python20
    题目:一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3.编程找出1000以内的所有完数。程序分析:请参照程序Python练习实例14。程序源代码:实例#!/usr/bin/python#-*-coding:UTF-8-*-fromsysimportstdoutforjinrange(2,1001):k=[]n=-1s=jfor......
  • Python 练习实例21
    题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。程序分析:采取逆向思维的方法,从后往前推断。程......
  • Python入门系列16-os模块
    一、os库介绍os库提供了操作系统功能接口函数,可以操作系统相关变量、文件和目录相关操作、路径相关操作。二、系统变量相关操作1、os.name:返回操作系统类型importosprint(os.name)输出:nt2、os.environ返回系统的环境变量importosprint(os.environ)3、os.sep:返回系统路径......
  • Python入门系列16-os模块
    一、os库介绍os库提供了操作系统功能接口函数,可以操作系统相关变量、文件和目录相关操作、路径相关操作。二、系统变量相关操作1、os.name:返回操作系统类型importosprint(os.name)输出:nt2、os.environ返回系统的环境变量importosprint(os.environ)3、os.sep:返回系统路径......
  • Python画一只懒羊羊
    在dy上看见的懒羊羊,原文链接:https://zhuanlan.zhihu.com/p/619060564importturtledefplotLine(points,pencolor=None,width=None,speed=None):'''功能:画折线参数:-points:一系列点,用列表或元组表示-pencolor:画笔颜色,默认不变-wid......
  • Python - 字典1
    字典用于存储键值对形式的数据。字典是一个有序、可更改的集合,不允许重复。从Python3.7版本开始,字典是有序的。在Python3.6及更早版本中,字典是无序的。字典用花括号编写,具有键和值:示例,创建并打印一个字典:thisdict={"brand":"Ford","model":"Mustang","year":......
  • python: openpyxl操作Excel
    1、安装pipinstallopenpyxl想要在文件中插入图片文件,需要安装pillow,安装文件:PIL-fork-1.1.7.win-amd64-py2.7.exe·font(字体类):字号、字体颜色、下划线等·fill(填充类):颜色等·border(边框类):设置单元格边框·alignment(位置类):对齐方式......
  • 如何定制化跑腿小程序源码
    跑腿小程序源码为您提供了一个强大的起点,但要创建一个成功的本地服务平台,您通常需要对源码进行定制化。这篇文章将介绍如何定制化跑腿小程序源码,包括添加新功能、修改界面和优化用户体验。选择合适的跑腿小程序源码首先,您需要选择适合您需求的跑腿小程序源码。这可以是开源项目、商......
  • 彻底删除虚拟机VMware(win 10)
    1.打开服务2.找到VMware开头的服务,右键【停止】3.使用Crtl+Shift+Esc打开任务管理器,找到以VMware打头命名的进程,右键【结束任务】4.在打开控制面板,点击【卸载程序】5.找到VMware6.右键更改7.点击【下一步】8.点击【删除】9.点击【下一步】10.点击【删......
  • 【分享】讯飞星火认知大模型Python调用上下文测评
    一个很常用的用法,先是system提示,然后是user问题{"role":"system","content":"假设你是个程序员,你的微信是xxxxxxxx"},{"role":"user","content":"微信多少"}openai测试importopenai#pipinstallopenaiop......