GIL全局解释器锁

文章目录

  • GIL全局解释器锁
    • 一、引入:
    • 二、常用的Python解释器种类有哪些?
      • 1、CPython
      • 2、IPython
      • 3、PyPy
      • 4、Jython
      • 5、IronPython
    • 三、GIL介绍
    • 四、GIL与Lock
    • 五、GIL与多线程
    • 总结

GIL全局解释器锁

一、引入:

首先要明白,GIL并不是Python的一个特性,其实在我们通常所称呼的Python解释器,其实是CPython解释器,因为大部分Python程序都是基于该解释器执行的,当然还有JPython解释器(基于Java编写的),而这个GIL则是CPython解释器的特性,而不是Python的特性。

GIL全称:Global Interpreter Lock,官方解释

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

结论:在CPython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

二、常用的Python解释器种类有哪些?

1、CPython

当从Python官方网站下载并安装好Python2.7后,就间接获得了一个官方版本的解释器:Cpython,这个解释器是用C语言开发的,所以叫CPython,在命名行下运行python,就是开始CPython解释器,CPython是使用尤其广的Python解释器。

2、IPython

IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,不过执行Python代码的功能和CPython是完全一样的,好比特别多国产浏览器虽然外观不同,不过内核其实是调用了IE。

3、PyPy

PyPy是另一个Python解释器,它的目标是执行速度,PyPy采用JIT技术,对Python代码做到动态编译,所以不妨显著提高Python代码的执行速度。

4、Jython

Jython是运行在Java途径上的Python解释器,不妨间接把Python代码编译成Java字节码执行。

5、IronPython

IronPython和Jython相似,只不过IronPython是运行在微软.Net途径上的Python解释器,不妨间接把Python代码编译成.Net的字节码。

综上所述,是有关python的五种常用的编程器,在Python的解释器中,使用广泛的是CPython,对于Python的编译

三、GIL介绍

GIL本质上也是一把互斥锁,既然是互斥锁,本质上都是将并发变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

GIL达到的效果则是,同一时间内只有一个线程能够拿到GIL锁,拿到GIL锁后,该线程就可以使用解释器进行操作。

用一个小例子说明GIL的小部分作用:

没有GIL锁的话,如果有两个线程,一个线程是给变量添加数据,另一个线程则是垃圾回收机制的线程,此时可能就会出现一个问题,一个线程在定义数据产生后,还没有来的及将该数据的内存地址绑定给变量,就被垃圾回收机制给回收了(因为检测到这个数据计数为0),由于都是同一进程下的线程,且还是并发执行的,如果真的是这样执行,将给我们的程序造成很大的隐患。

四、GIL与Lock

可以存在的疑惑,既然有了GIL锁来保证同一时间只能有一个线程运行,那还需要Lock锁干嘛?

首先,我们需要达成共识:锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据

然后,我们可以得出结论:保护不同的数据就应该加不同的锁。

最后,问题就很明朗了,GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock

GIL保护的是解释器级别的数据,保护用户自己的数据则需要自己加锁处理,如下图:

在这里插入图片描述
再来分析我们的疑问:

  1. 如果现在存在100个线程,那么其中某一个线程会抢到GIL锁,我们且称它为:线程1。
  2. 当线程1拿到GIL可以使用解释器后,线程1使用一个函数对全局变量count进行+1操作之前进行了互斥锁Lock。
  3. 而此时线程1还未执行完+1操作,线程2就把GIL锁给抢走了,此时线程2拿到这个GIL也想来执行这个+1操作,但执行这个函数时,发现有一个Lock未被释放,那它只能阻塞住,被释放GIL。
  4. 当线程1再次抢到这个GIL后,继续在上一次暂停的位置完成的+1操作,做完以后,再把Lock、GIL给释放掉。此后其它线程重复1、2、3、4步骤

保护不同的数据就需要加不同的锁

五、GIL与多线程

有了GIL的存在,同一时刻同一进程中只有一个线程被执行

创建进程开销大、而线程开销小,却无法利用多核优势,Python不行了?
要解决这个问题,我们需要在几个点上达成一致:

  1. cpu到底是用来做计算的,还是用来做I/O的?
  2. 多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能
  3. 每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处

所以我们Python中,多线程是并发操作的,而多进程是可以做到并行操作的。

那么在Python里遇到I/O操作时,线程所用资源以及速度都会优于进程的。

而如果计算的话,多进程则更占据优势

代码示例:计算密集型,多进程效率更高

from multiprocessing import Process
from threading import Thread
import os, timedef work():res = 0for i in range(100000000):res *= iif __name__ == '__main__':l = []print(os.cpu_count())  # 本机为8核start = time.time()for i in range(4):p = Process(target=work)  # 多进程、耗时6s多# p = Thread(target=work)  # 多线程、耗时19s多l.append(p)p.start()for p in l:p.join()stop = time.time()print('run time is %s' % (stop - start))

代码示例:I/O密集型,模拟I/O操作等待时间,线程并发效率更高

from multiprocessing import Process
from threading import Thread
import os, timedef work():time.sleep(4)if __name__ == '__main__':l = []print(os.cpu_count())  # 本机为8核start = time.time()for i in range(4):# p = Process(target=work)  # 多进程、耗时4.1s左右p = Thread(target=work)  # 多线程、耗时4.005s左右l.append(p)p.start()for p in l:p.join()stop = time.time()print('run time is %s' % (stop - start))

因为开启进程的开销会远大于开启线程,而做这种I/O较多的操作,多线程会更具有优势

应用场景举例:

多线程用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析

总结

  1. 因为GIL的存在,只有IO较多场景下的多线程会得到较好的性能。
  2. 如果对并行计算性能较高的程序可以考虑把核心部分也成C模块,或者使用多进程实现。
  3. GIL在较长一段时间内将会继续存在,但是会不断对其进行改进。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/144865.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工作中几个问题的思考

对于需要并行多公司并行处理的任务,方案是什么? 多线程、并行流、并发库(ExecutorService、Futrue、Callable),分布式计算(1)按照公司ID分片 (2)按照业务类型分片 处理…

【驱动开发】创建设备节点、ioctl函数的使用

一、控制三盏灯的亮灭 头文件: #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t; #define PHY_LED1_ADDR 0X50006000 #def…

Apache Jmeter测压工具快速入门

Jmeter测压工具快速入门 一、Jmeter介绍二、Jmeter On Mac2.1 下载2.2 安装2.2.1 环境配置2.2.2 初始化设置 2.3 测试2.3.1 创建JDBC Connection Configuration2.3.2 创建线程组2.3.3 创建JDBC Request2.3.4 创建结果监控2.3.5 运行结果 2.4 问题记录2.4.1 VM option UseG1GC异…

Power BI 傻瓜入门 5. 准备数据源

本章内容将介绍: 定义Power BI支持的数据源类型探索如何在Power BI中连接和配置数据源了解选择数据源的最佳做法 现代组织有很多数据。因此,不用说,微软等企业软件供应商已经构建了数据源连接器,以帮助组织将数据导入Power BI等…

PAM从入门到精通(二十)

接前一篇文章:PAM从入门到精通(十九) 本文参考: 《The Linux-PAM Application Developers Guide》 先再来重温一下PAM系统架构: 更加形象的形式: 七、PAM-API各函数源码详解 前边的文章讲解了各PAM-API函…

图像信号处理板设计原理图:2-基于6U VPX的双TMS320C6678+Xilinx FPGA K7 XC7K420T的图像信号处理板

综合图像处理硬件平台包括图像信号处理板2块,视频处理板1块,主控板1块,电源板1块,VPX背板1块。 一、板卡概述 图像信号处理板包括2片TI 多核DSP处理器-TMS320C6678,1片Xilinx FPGA XC7K420T-1FFG1156,1片X…

Linux进程(四)--进程地址空间(一)

前言:在Linux中,每个正在运行的进程都有自己独立的虚拟地址空间,该虚拟地址空间是逻辑上的抽象,用于在进程间提供隔离和保护。它将进程的内存分配和访问从物理内存中分离出来,为每个进程提供了一个独立的地址空间。这究…

零基础搭建个人网站详细流程

最近两天,为了给自己的工具类APP备案,买了阿里云ECS和域名。虽然很想说离线工具APP不用联网,但是现实就很无语。言归正传,既然买了总不能将它们闲置着,就诞生了建站的想法,至少还能放个用户协议和隐私协议。…

FFmpeg和rtsp服务器搭建视频直播流服务

下面使用的是ubuntu的,window系统可以参考: 通过rtsp-simple-server和ffmpeg实现录屏并发布视频直播_rtsp simple server_病毒宇宇的博客-CSDN博客 一、安装rtsp-simple-server (1)下载rtsp-simple-server 下载地址:R…

二、BurpSuite Intruder暴力破解

一、介绍 解释: Burp Suite Intruder是一款功能强大的网络安全测试工具,它用于执行暴力破解攻击。它是Burp Suite套件的一部分,具有高度可定制的功能,能够自动化和批量化执行各种攻击,如密码破解、参数枚举和身份验证…

时序分解 | Matlab实现CEEMD互补集合经验模态分解时间序列信号分解

时序分解 | Matlab实现CEEMD互补集合经验模态分解时间序列信号分解 目录 时序分解 | Matlab实现CEEMD互补集合经验模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现CEEMD互补集合经验模态分解时间序列信号分解 1.分解效果图 &#xff0…

Linux高性能编程学习-TCP/IP协议族

一、TCP/IP协议族结构与主要协议 分层:数据链路层、网络层、传输层、应用层 1. 数据链路层 功能:实现网卡驱动程序,处理数据在不同物理介质的传输 协议: ARP:将目标机器的IP地址转成MAC地址RARP:将MAC地…