Skip to content

Latest commit

 

History

History
101 lines (71 loc) · 5.51 KB

GIL进程以及线程.md

File metadata and controls

101 lines (71 loc) · 5.51 KB

进程、线程、GIL(Global Interpreter Lock)的由来

进程

CPU是资源,内存也是资源,资源特点是稀少,需要竞争。

计算机里什么需要这些资源呢?当然是应用,那他们通过什么来竞争这些资源呢?就是进程。

应用必然需要运行,运行就需要依靠CPU来做,所以每个应用至少有一个进程,进程是资源竞争的最小单位。

线程

随着计算机的进步,CPU的速度越来越快,以进程为单位管理CPU资源粒度太大了,也就是说,用进程抢占到的 CPU资源不能被进程充分的利用。

比如一个进程抢占的CPU资源其实可以执行10万行代码,实际只利用CPU执行了2万行代码, 那这就是一种资源利用率低的情况。

为了解决这个问题,需要引入一个更加灵活轻量的机制来协调使用这些进程的资源,这就是线程。 这样也决定了线程是属于进程的,一个进程可以有若干个线程。

GIL

从这里看,进程与线程的分工不同:

  • 进程:分配资源,内存及CPU
  • 线程:使用资源来执行代码(代码->指令->CPU执行->使用内存资源)

而多个线程都是要使用进程资源的,这也就造成了,线程间资源共享的问题,一旦出现共享, 那就有可能会造成资源是线程不安全的。

线程不安全:两个线程同时使用某个不能同时访问的资源,由于线程的推进时间的先后不同而产生的问题。 也就是出现race condition,最后可能导致这个变量的不正确。

所以出现了GIL。GIL做了什么呢?

不管你的电脑是几核的,也不管你开了多少个线程, GIL使得你的Python代码同一时刻只能在一个核上执行一个线程。所以多线程的时候执行情况如下图: GIL在执行中的情况

你可能会发现一个问题:为什么 Python 线程会去主动释放 GIL 呢?

如果仅仅是要求 Python 线程在开始执行时锁住 GIL,而永远不去释放 GIL,那别的线程就都没有了运行的机会。 这跟单线程有什么区别呢?

所以GIL中还有另一个机制,叫做 check_interval,意思是Python解释器会去轮询检查线程 GIL 的锁住情况。 每隔一段时间,Python 解释器就会强制当前线程去释放GIL,这样别的线程才能有执行的机会。

关于锁,GIL真的确保了线程安全吗?

锁的出现就是为了解决线程安全问题。锁也分细粒度的锁和粗粒度的锁。

细粒度的锁:可以简单地理解为程序员自己加的锁

粗粒度的锁:解释器层面加了锁-->GIL,所以不管你的电脑硬件条件如何,python解释器决定了 pyhton的代码是单核单线程运行的。

这样看似确保了线程安全,但是实际上,正因为GIL是解释器层面的,所以导致了一些不安全。 为什么这么说呢?

因为代码会先变成字节码再执行,而GIL又有check_interval,这就意味着, 在A线程的某段字节码执行到中间的某行就可能会被打断去执行别的线程的字节码,这是解释器层面 可能发生的事情,是不可控的,所以GIL不是绝对安全。

这就是为什么我们程序员也要在代码里使用Lock来加锁,来保证线程安全,不能只依靠GIL。

进程/线程切换

还有操作系统管理着各种应用和进程,那么进程之间切换的时候会消耗一些资源,尤其是保存原进程的状态会 有很大的消耗,我们当然希望这些资源充分利用在执行程序上,而不是消耗在切换上。

而线程不管理资源,不拥有资源,所以线程切换要比进程切换轻量太多了,这也给多线程编程创造了条件。

关于Pyhton的多线程有何意义

python是靠解释器解释代码的,cPython解释器有GIL,jpython没有GIL,站在线程的角度考虑这个问题, cpython是没办法很好利用CPU多核优势的。

虽然可以借助多进程,但是多进程又有多进程的问题,比如进程间通信,技术复杂且进程切换开销太大。

所以在这样的情况下,可能很多人诟病python的多线程是没有意义的,但实际上它还是有意义的。

这里有两个概念,CPU密集型程序和IO密集型程序。所谓CPU密集型程序,如数学计算。 而大多数程序员编写的代码都是IO密集型程序,比如数据库查询,请求网络资源,文件读写, web应用的瓶颈一般都在查询数据库。

IO密集型程序主要时间都花在等待,比如等待数据库返回结果,等待网络请求返回结果,在这种时候 它并不需要占用CPU资源,所以这个时候,这种等待的线程就可以让出来CPU资源,交给别的线程去使用 CPU,这就是多线程在IO密集型程序里的优势。

如果我们写的大多程序使IO密集型的,那么python的多线程还是非常有意义的。

cpython解释器不适合做CPU密集型程序,因为CPU密集型程序基本每时每刻都要CPU参与计算,不会有 等待时间。

补充说明

GIL存在的原因:

  1. 规避race condition
  2. cPython 大量使用C语言库,大部分C语言都不是原生线程安全的(线程安全会降低性能和增加复杂度)

它是为了方便cPython解释器层面的编写者,而不是使用python语言的程序员