GIL
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management is not thread-safe.
Python是一门解释器语言,代码想运行,必须通过解释器执行,Python存在多种解释器,分别基于不同语言开发,每个解释器有不同的特点,但都能正常运行Python代码,以下是常用的五种Python解释器:
CPython
当从Python官方网站下载并安装好Python2.7后,就直接获得了一个官方版本的解释器:Cpython,这个解释器是用C语言开发的,所以叫CPython,在命名行下运行python,就是启动CPython解释器,CPython是使用最广的Python解释器。
IPython
IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的,好比很多国产浏览器虽然外观不同,但内核其实是调用了IE。
PyPy
PyPy是另一个Python解释器,它的目标是执行速度,PyPy采用JIT技术,对Python代码进行动态编译,所以可以显著提高Python代码的执行速度。
Jython
Jython是运行在Java平台上的Python解释器,可以直接把Python代码编译成Java字节码执行。
IronPython
IronPython和Jython类似,只不过IronPython是运行在微软.Net平台上的Python解释器,可以直接把Python代码编译成.Net的字节码。
在Python的解释器中,使用广泛的是CPython,对于Python的编译,除了可以采用以上解释器进行编译外,技术高超的开发者还可以按照自己的需求自行编写Python解释器来执行Python代码,十分的方便!
GIL本质也是一把互斥锁,将并发变成串行牺牲效率保证数据的安全,用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行,但是可以实现并发)
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
GIL的存在是因为CPython解释器的内存管理不是线程安全的,就是多个线程访问同一个资源,会有问题,CPython中有一个互斥锁,防止线程同一时间执行python代码
C编译过的结果可以计算机直接识别
垃圾回收机制
1.引用计数:内存中的数据如果没有任何的变量名与其有绑定关系,那么会被自动回收
2.标记清除:当内存快要被某个应用程序占满的时候,会自动触发
3.分代回收:根据值的存活时间的不同,划为不同的等级,等级越高垃圾回收机制扫描的频率越低
GIL是保证线程的安全,多个线程之间不会产生冲突,因为他同一时间只能执行一个任务
分代回收
自动垃圾回收其实就是说,内部会有一个垃圾回收线程,会在某一时间运行起来,开始清理垃圾
这是可能会产生问题,例如线程1申请了内存,但是还没有使用CPU切换到了GC,GC将数据当成垃圾清理掉了
Cpython诞生于1991年 而多核处理器诞生2004年
当时不需要考虑多核效率问题
释放:
该线程任务结束
该线程遇到IO
多线程不能并行
案例:
有一个下载任务 要从网络中下载一个文件 大小1G
和转换 任务 使用input 转为大写输出
上述任务并行执行,耗时也不会有太大的提升,反而开启多进程会浪费更多资源
这种任务称之为IO密集型,大量的时间都花在IO等待
这类问题使用多线程,
计算密集型任务
图像处理,语音处理,大数据分析,
区分任务类型
如果是IO密集使用多线程
如果是计算密集使用多进程
计算密集型 from multiprocessing import Process from threading import Thread import os,time def work(): res = 0 for i in range(100000000): res*=i if __name__ == ‘__main__‘: l = [] print(os.cpu_count()) start = time.time() for i in range(8): # p = Thread(target=work) p = Process(target=work) l.append(p) p.start() for p in l: p.join() stop = time.time() print(‘run time is %s‘ %(stop-start)) IO密集型 from multiprocessing import Process from threading import Thread import threading import os,time def work(): time.sleep(2) if __name__ == ‘__main__‘: l = [] print(os.cpu_count()) start = time.time() for i in range(4000): # p= Process(target=work) #run time is 221.63026809692383 p = Thread(target=work) #run time is 2.3937768936157227 l.append(p) p.start() for p in l: p.join() stop=time.time() print(‘run time is %s‘%(stop-start))
GIL与普通的互斥锁
都是互斥锁
为什么有了GIL还需要自己加锁
GIL是加在解释器上的,只能锁住,解释器内部的资源,但是无法锁住我们自己开启资源
例如: 我们自己开启了一个json文件,多个线程要同时访问, 如果第一个线程写入数据写到一半,执行超时了,另一个线程过来接着写,就会产生问题,
自己开启共享资源还得自己所锁
像是申请内存 保存数据等等就不需要我们程序员考虑了 GIL已经搞定了
from threading import Thread import time n = 100 def task(): global n tmp = n n = tmp - 1 t_list = [] for i in range(100): t = Thread(target=task) t.start() t_list.append(t) for t in t_list: t.join() print(n)
死锁
可以被第一个抢到锁的人连续的acquire和release,每acquire一次锁身上的计数加1,每release一次锁身上的计数减1,只要锁的计数不为0,其他人都不能抢
from threading import Thread,Lock,current_thread,RLock import time mutexA = Lock() mutexB = Lock() # mutexA = mutexB = RLock() #A B现在是同一把锁 class MyThread(Thread): def run(self): #创建线程自动触发run方法,run方法内调用func1 func2相当于也是自动触发 self.func1() self.func2() def func1(self): mutexA.acquire() print(‘%s抢到了A锁‘ % self.name) #self.name等价于current_thread().name mutexB.acquire() print(‘%s抢到了B锁‘ %self.name) mutexB.release() print(‘%s释放了B锁‘ %self.name) mutexA.release() print(‘%s释放了A锁‘ %self.name) def func2(self): mutexB.acquire() print(‘%s抢到了B锁‘%self.name) time.sleep(1) mutexA.acquire() print(‘%s抢到了A锁‘%self.name) mutexA.release() print(‘%s释放了A锁‘%self.name) mutexB.release() print(‘%s释放了B锁‘%self.name) for i in range(10): t = MyThread() t.start()
解锁 from threading import Thread,Lock,current_thread,RLock import time # mutexA = Lock() # mutexB = Lock() mutexA = mutexB = RLock() #A B现在是同一把锁 class MyThread(Thread): def run(self): #创建线程自动触发run方法,run方法内调用func1 func2相当于也是自动触发 self.func1() self.func2() def func1(self): mutexA.acquire() print(‘%s抢到了A锁‘ % self.name) #self.name等价于current_thread().name mutexB.acquire() print(‘%s抢到了B锁‘ %self.name) mutexB.release() print(‘%s释放了B锁‘ %self.name) mutexA.release() print(‘%s释放了A锁‘ %self.name) def func2(self): mutexB.acquire() print(‘%s抢到了B锁‘%self.name) time.sleep(1) mutexA.acquire() print(‘%s抢到了A锁‘%self.name) mutexA.release() print(‘%s释放了A锁‘%self.name) mutexB.release() print(‘%s释放了B锁‘%self.name) for i in range(10): t = MyThread() t.start()
信号量
信号量可能在不同的领域中,对应不同的知识点
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
from threading import Semaphore,Thread import time import random sm = Semaphore(5) #造了一个含有五个的坑位的公共厕所 def task(name): sm.acquire() print(‘%s占了一个坑位‘%name) time.sleep(random.randint(1,3)) sm.release() for i in range(40): t = Thread(target=task,args=(i,)) t.start()
event事件
from threading import Event,Thread import time #先生成一个event对象 e = Event() def light(): print(‘红灯正亮着‘) time.sleep(3) e.set() #发信号 print(‘绿灯亮了‘) def car(name): print(‘%s正在等红灯‘%name) e.wait() #等待信号 print(‘%s加油门飙车了‘%name) t = Thread(target=light) t.start() for i in range(10): t = Thread(target=car,args=(‘伞兵%s‘%i,)) t.start()
线程b
import queue q = queue.Queue() q.put(‘hahaha‘) print(q.get()) q = queue.LifoQueue() q.put(1) q.put(2) q.put(3) print(q.get()) q = queue.PriorityQueue() #数字越小,优先级越高 q.put((10,‘haha‘)) q.put((100,‘hehe‘)) q.put((0,‘xxx‘)) q.put((-10,‘yyy‘)) print(q.get())
原文:https://www.cnblogs.com/pangqian/p/11353468.html