首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。
就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。
>有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。
像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL
只有CPython 有GIL全局解释器锁
二 GIL介绍
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
可以肯定的一点是:保护不同的数据的安全,就应该加不同的锁。
执行python程序默认会生成一个进程 这个进程就是这个python解释器程序
#! /usr/bin/env python
# -*- coding: utf-8 -*- import time
import os print os.getpid()
time.sleep(1000)
[root@salt-server-192 ~]# python create_process.py
1343
#在windows下查看
tasklist |findstr python #在linux下下查看
ps -aux |grep python
在一个python的进程内,不仅有这个程序的的主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一个进程内
1、所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码)
例如:test.py定义一个函数work,在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问到意味着就是可以执行。
2、所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。
接上:
如果多个线程的target=work,那么执行流程是
多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行
1.每起一个进程,在内存空间都会默认生成一个主线程
线程的代码 taget参数 指向python代码 。然后把python代码,当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。
图片上的参数就是python代码
2.进程内可以开好多个线程
CPython里面有一个垃圾回收机制 也是执行解释器代码,所以在进程内,内存空间会有一个垃圾回收线程
垃圾回收线程运行的是python解释器代码
因为进程内有4个线程,垃圾回收线程和其他线程会同时执行,垃圾回收线程有可能和其他线程工作冲突,导致数据不安全,
所以要控制 这些线程一个个运行 需要在CPython解释器里加锁
解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码
加了互斥锁每个线程要运行,每个线程抢着加锁, 一次执行 只能加锁require() 一次 再加锁require() 一次就变成阻塞状态 要等到释放release() 之后 再加锁require(),
同一时间只能有一个线程抢到锁,其他线程都要等待
1.GIL全局解释器锁本质就是互斥锁,加了GIL全局解释器锁 同时运行的多个线程,变成一个一个运行 ,效率低了, 但保证数据安全
2.只有Cpython解释器才有GIL ,有了互斥锁,对CPython 解释器来说 每启动一个进程 这个进程内的多个线程 同一时间只能有一个在运行
3.也就是说python多线程不能用多核优势,在CPython 想用多核只能用多进程 单核只适合多线程
4.垃圾回收机制是否一直存在?
CPython的垃圾回收机制 定时销毁 定时启动的