python之GIL锁详解

目录

1.GIL是什么以及影响

2.为什么会有GIL锁?


1.GIL是什么以及影响

在Python中,多线程的并发性受到全局解释器锁(GIL,Global Interpreter Lock)的影响。GIL是CPython(Python的官方实现)中的一个特性,它的主要目的是确保在任何时候只有一个线程在执行Python字节码。

这是因为CPython的内存管理不是线程安全的,所以需要一个锁来防止多个线程同时修改或访问共享的内存区域。

然而,这并不意味着Python多线程不能实现真正的并发。GIL允许线程之间进行切换,但每次只有一个线程能够持有GIL并执行Python字节码。当线程需要执行I/O操作或调用某些内置函数(如time.sleep)时,它可能会释放GIL,允许其他线程获得执行权。这种切换机制使得Python多线程在某些情况下(如I/O密集型任务)仍然可以实现并发性能的提升。

但是,对于计算密集型任务,Python多线程可能无法充分利用多核CPU的优势,因为GIL限制了同一时间只有一个线程能够执行Python字节码。在这种情况下,使用多进程(multiprocessing)可能是更好的选择,因为多进程允许每个进程拥有自己的GIL和内存空间,从而实现真正的并行执行。

总之,GIL的作用是确保CPython的内存管理在多线程环境中的线程安全性,但它也限制了Python多线程在计算密集型任务中的并发性能。因此,在选择使用多线程还是多进程时,需要根据具体的任务类型和性能需求进行权衡。

GIL并不是Python的特性,Python完全可以不依赖于GIL。

2.为什么会有GIL锁?

原因在于python的内存管理机制采用引用计数机制,引用计数这个变量不是线程安全的,需要用GIL全局锁来进行数据保护。

python中的对象使用引用计数为主,标记清楚和隔代回收为辅来进行内存管理。所有python脚本中创建的对象,都会配备一个引用计数,来记录有多少个指针来指向它。以全局变量a为例,每有一个线程若调用了a,则a的引用计数加1。

注意:全局变量的引用计数并不直接与线程数量相关。全局变量(或任何Python对象)的引用计数仅与其被引用的次数有关。比如有10个线程,但只有9个线程引用该变量,则引用计数为9。

当一个线程引用一个全局变量时,该变量的引用计数增加;当引用被删除(比如线程结束使得引用被删除)或超出作用域(比如局部变量的结束)时,引用计数减少。另外每个线程可以独立地引用或取消引用全局变量,从而影响其引用计数。

当对象的引用计数为0时,Python的垃圾回收器会释放该对象占用的内存。但是,请注意,这并不意味着对象占用的内存会立即返回给操作系统;它可能会被Python的内存管理器保留以供将来使用。

那么为什么需要GIL锁呢,因为这个引用计数变量不是线程安全的,举例说明如下:

  1. 假设开始时只有主线程,引用一个数据(a=100,引用计数x为1),
  2. 若再开启2个python子线程,每个线程的开启都会使得x进行自增1的操作(x+=1),意味着有多个线程对同一个资源的竞争,如果有GIL锁存在的话,x最后正常会变成3
  3. 但是如果没有GIL锁的话,x可能最终是2。这造成的后果是,当第1个子线程结束时,会把引用计数x减少为1;当第2个线程结束时,会把引用计数x减少为0,这时变量a会被释放所占用的内存。之后若主线程再次试图访问a这个数据时,将会程序异常,会无法找到有效的内存,程序会出错。

总结GIL存在的目的:GIL的存在是为了防止在多个线程同时执行Python字节码时,由于内存管理(如引用计数)的并发操作导致的竞态条件。GIL确保在任何时候只有一个线程在执行Python字节码。


end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值