Python 的GIL 和 互斥锁

GIL

有 Python 开发经验的人也许听说过这样一句话:Python 不能充分利用 CPU 的多核优势

为什么呢?

 因为Python(Cpython) 中存在 GIL,即global interpreter lock(全局解释器锁)。用于限制一个进程中同一时刻只有一个线程被CPU调度。Python 程序尽管也支持多线程,但由于受到 GIL 的保护,所以同一时刻,只有一条线程可以向前执行。

接下来我们详细谈谈 Python 中存在的 GIL。

标准的 Python 实现叫做 CPython。CPython 分两步来运行 Python 程序。

  1.  把文本形式的源代码解析并编译成字节码。
  2.  用一种基于栈的解释器来运行这份字节码。

执行 Python 程序时,字节码解释器必须保持协调一致的状态。那么怎么才能够保持协调一致的状态呢?

Python 采用 GIL 机制来确保这种协调性。

GIL 实际就是一把互斥锁,用以防止 CPython 受到占先(抢占)式多线程切换操作的干扰。所谓占先式多线程切换,是指某个线程可以通过打断另外一个线程的方式,来获取程序控制权。

假如这种干扰操作的执行时机不恰当,那就会破坏解释器的状态。

正是因为存在 GIL,那么这些干扰操作就不会发生了。GIL 可以保证每条字节码指令能够正确的与 CPython 实现及其 C 语言扩展模块协同运作。

 

既然 Python 不能充分利用 CPU 的多核优势,那么为什么还要支持多线程呢?

这是因为:

① 多线程使得程序看上去好像能够在同一时间做许多事情。

借助多线程,则能够令 Python 程序自动以一种看似平行的方式来执行线程调用的函数。这是因为 CPython 在执行 Python 线程的时候,可以保证一定程度的公平,但不是绝对的公平。由于受到 GIL 的限制,所以统一时刻实际上只能有一个线程得到执行。

② 在处理阻塞式的 I/O 操作,Python 在执行某些系统调用时,会触发多线程操作。

我们知道读写文件、在网络间通信、以及与显示器等设备相交互等,都属于阻塞式的 I/O 操作。为了响应这种阻塞式的请求,操作系统必须花一些时间,开发者可以借助多线程,把 Python 程序与这些耗时的 I/O 操作隔离开。

尽管受制于 GIL,但是用多个 Python 线程来执行系统调用的时候,这些系统调用可以可以平行的执行。GIL 虽然使得 Python代码无法并行,但它对系统调用却没有任何负面影响。

 

 

互斥锁

明白了 GIL 机制之后,也许你会认为在编写Python 代码时,是不是就不需要再使用互斥锁了?

NO !

实际上 GIL 并不会保护开发者自己编写的代码。这是因为同一时刻固然只能有一个 Python 线程得到执行,但是,当这个线程正在操作某个数据结构的时候,其他线程还是可能会打断它,一旦发生这种现象,就会破坏程序的状态,从而使相关的数据结构无法保持其一致性。为了保证所有线程能够得到公平地执行,Python 解释器会给每个线程分配大致相等的处理器时间。为了达到这样的分配策略,Python 系统可能当某个线程正在执行的时候将其暂停,然后使另一个线程继续往下执行。由于我们无法提前获知 Python 系统会在何时暂停这些线程,所以我们无法控制程序中某些操作是原子操作。

为了防止线程中出现数据(共享资源)竞争的行为,使开发者可以保护自己的数据结构不受破坏,Python 在 threading 模块中提供了最简单、最有用的工具:Lock 类,该类相当于互斥锁。

在开发中我们可以使用互斥锁来保护某个对象,使得在多线程同时访问某个对象的时候,不会将该对象破坏。因为同一时刻,只有一个线程能够获得这把锁。也就是说对将要访问的对象进行隔离,那么使用线程隔离的意义在于:是当前线程能够正确的引用到它自己创造的对象,而不是引用到其它线程锁创建的对象。

讲到这里你应该明白:在 Python 开发中多线程不在是鸡肋的原因了

 

注意:

为什么gil没有被删除?因为删了gil会使得Python3单线程任务方面比Python2慢。

每个Python进程都有自己的Python解释器和内存空间,所以gil不会是问题(可以采用多进程代替多线程)。

替换Python解释器,gil只存在于cpython中。

在处理科学计算这类要持续使用CPU的任务时,单线程会比较快。

处理io操作等引起阻塞这类任务时,多线程会比单线程快。

锁的好处,确保了某段代码只能由一个线程从头到尾的完整执行。

锁的坏处, 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行。效率大大降低。由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁就会造成死锁。

多线程程序的执行顺序是不确定的。

 

总结:

GIL: 保证某时刻某进程中只有一个线程被执行。

互斥锁:保证共享资源的一致性(如经典的全局变量多线程操作时的数据安全性)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值