对于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锁的 一个关系