python关于多线程的GIL问题,以及CPU分配核数的问题

感情迁移 提交于 2019-12-05 20:41:12

对于Python中,多线程的问题详细描述:

        在Python中,其实对于多线程的运行方案并不完美,纯属的Python多线程运行时,只能实现并发执行,对于现在的多核CPU来说,有点浪费CPU资源,但在其他的语言中,并没有这个问题。

    这一切都是由于时代的原因,在上个世纪80年代,由于硬件的发展,当时的电脑只是单核CPU,并没有今天的多核CPU。发明Python语言的龟叔,为了实现单核多任务的操作,提出了一个相当精彩的概念,就是GIL。

    GIL解决了单核CPU多任务的问题,但随着硬件的发展,同样也暴露出了GIL的缺点,即不能实现在多核CPU的今天,多线程的多任务并行执行。

为了探讨GIL的实现,先简单简绍一下多任务的概念:

    对于多任务,无论是多进程或者多线程,在具体的CPU执行阶段是按线程去分配CPU执行的,因为在CPU执行阶段会将进程分为线程执行每一个进程可以细分为一个主线程和n个子线程,在真正执行的时候,是由线程去争抢CPU的时间片, 即CPU分配资源和调度的基本单位是线程。通过时间片轮流机制实现多任务的执行效果。具体的时间片轮流机制可以查阅CPU的制造商的相关网站,每家的厂商对于时间片轮流机制的算法不尽相同,但都实现了同一个功能,即让CPU不停的切换多个线程,并让每个线程执行一个或者多个时间片(即是该线程竞争到的时间片,如果在该线程执行时遇到I/O操作,CPU会释放该线程资源,转而执行其他竞争到CPU时间片的线程)。

GIL的出现与现在出现的问题:

    GIL(全局解释器锁),该概念提出了在同一个进程下的多线程的任务中,保证同一时刻只能一个线程使用CPU,其他线程处于锁定状态,即其他线程不能去竞争CPU资源,或者说其他线程去竞争CPU资源,即使竞争到也会被CPU调度释放。GIL概念提出:在同一进程下的多线程执行时,只有拥有GIL锁的一个线程能够在CPU中进行运算执行,如果该线程在时间片用完时,或者在执行时遇到I/O操作,会释放GIL锁 ,转而将锁发放给其他竞争到CPU资源的线程拥有。

    因此,在哪个单核年代,龟叔的GIL概念算是完美了解决单核多任务的想法。但在计算机飞速发展的今天,随着多核CPU的出现,GIL反而成为了Python的弱点,即单进程多任务不能实现多任务的并行执行,降低了CPU的使用效率。即同一时间只能有一个线程执行。因此只能执行并发的方式,浪费了CPU的较高性能。


GIL是由解释器创建的。GIL本身也有执行的时间的规定和释放GIL锁资源的条件,是由龟叔在编写解释器时,,根据计算机性能去计算添加的,因此属于人为添加的。但在现在大多数的多任务(是指其他语言)是由编程语言编写,由CPU硬件去决定和分配资源去执行代码,而GIL是人为的添加CPU的执行。因此可以这么方便的解释Python的多任务,Python的多任务在进入执行时,不是像其他语言的是以线程进行CPU内核分配的,而是以进程为单位去进行CPU时间片资源竞争的。

以下是进程执行死循环时,竞争CPU一个核心资源的情况:



以下是一个进程下的线程,以进程为单位去竞争CPU资源,线程之间分配该线程竞争到的资源:

    看到现象好像是多个核心在同时执行,实际是只有一个核心在执行。但是由于CPU切换的资源的速度快到无法想象,因此你的眼睛欺骗了你。



多进程的执行情况



如何解决gil问题:

    1.更换cpython为jpython(不建议)

    2.使用多进程完成多线程的任务

    3.在使用多线程可以使用c语言去实现  参考:http://blog.csdn.net/cheryl_77/article/details/77160206

Gil全局解释器锁延伸扩展:


作用 : 保证同一时刻只有一个线程能使用到cup
解释 : 当我们使用多线程的时候,在一个进程中只有一个GIL锁,那么这多个线程中谁拿到GIL谁就可以
使用cpu(ps:多个进程有多个Gil锁)


问题1: 什么时候会释放Gil锁,
答 :  1 遇到像 i/o操作这种 会有时间空闲情况 造成cpu闲置的情况会释放Gil
  2 会有一个专门ticks进行计数 一旦ticks数值达到100 这个时候释放Gil锁 线程之间开始竞争Gil锁(说明:
ticks这个数值可以进行设置来延长或者缩减获得Gil锁的线程使用cpu的时间)

问题2: 互斥锁和Gil锁的关系

Gil锁  : 保证同一时刻只有一个线程能使用到cup
互斥锁 : 多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱


首先假设只有一个进程,这个进程中有两个线程 Thread1,Thread2, 要修改共享的数据date, 并且有互斥锁

执行以下步骤

(1)多线程运行,假设Thread1获得GIL可以使用cpu,这时Thread1获得 互斥锁lock,Thread1可以改date数据(但并
没有开始修改数据)

(2)Thread1线程在修改date数据前发生了 i/o操作 或者 ticks计数满100 (注意就是没有运行到修改data数据),这个
时候 Thread1 让出了Gil,Gil锁可以被竞争

(3) Thread1 和 Thread2 开始竞争 Gil (注意:如果Thread1是因为 i/o 阻塞 让出的Gil Thread2必定拿到Gil,如果
Thread1是因为ticks计数满100让出Gil 这个时候 Thread1 和 Thread2 公平竞争)

(4)假设 Thread2正好获得了GIL, 运行代码去修改共享数据date,由于Thread1有互斥锁lock,所以Thread2无法更改共享数据
date,这时Thread2让出Gil锁 , GIL锁再次发生竞争 


(5)假设Thread1又抢到GIL,由于其有互斥锁Lock所以其可以继续修改共享数据data,当Thread1修改完数据释放互斥锁lock,
Thread2在获得GIL与lock后才可对data进行修改

以上描述了 互斥锁和Gil锁的 一个关系

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!