GIL全局解释器锁
一、GIL介绍
GIL是一个互斥锁:将并发编程串行,降低了效率但保证了数据的安全
在Cpython解释器中才有的概念,不是python的特点
保护不同的数据安全,就应该加不同的锁
GIL全局解释器存在的原因是因为Cpython解释器的内存管理不是线程安全的
单进程下多个线程无法利用多核优势是所有解释型语言的通病
二、GIL解释
每次运行python解释器都会产生进程,进程下会有多个线程
如图所示:因为python解释器有垃圾回收机制,所以每次运行python解释器都会自带一个垃圾回收线程,假设该进程下还有3个线程,如果四个线程需要运行,需要去解释器那抢锁(GIL互斥锁),谁抢到了,谁就运行。这样就能防止,如果四个线程运行,线程1申请变量a =1 ,刚申请了一个变量,垃圾回收机制就把它回收。
三、GIL与多线程
研究python的多线程是否需有用需分情况讨论
四个任务:计算密集的任务,每个任务耗时10秒
单核情况下:
多线程好一点,消耗资源少一点
多核情况下:
开四个进程:10s多一点
开四个线程:40s多一点
from multiprocessing import Process from threading import Thread import os,time def work(): res=0 for i in range(10000000): res*=i if __name__ == '__main__': l=[] print(os.cpu_count()) # 本机为4核 start=time.time() for i in range(6): p=Process(target=work) # 多进程耗时 3.0867862701416016 # p=Thread(target=work) # 多线程耗时 5.1183037757873535 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()) #本机为4核 start=time.time() for i in range(100): # p=Process(target=work) # 多进程耗时13.326009750366211多,大部分时间耗费在创建进程上 p=Thread(target=work) # 多线程耗时2.0372188091278076多 l.append(p) p.start() for p in l: p.join() stop=time.time() print('run time is %s' %(stop-start))
from threading import Thread import time n = 100 def task(): global n tmp = n time.sleep(1) # 如果在此睡1秒,就会自动释放锁,其他99个人都会抢到锁,打印结果:99;如果不睡,那么此锁就会一直在第一个人手中,打印结果:0 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)
五、死锁现象与递归锁
1.死锁现象
from threading import Thread,Lock,current_thread,RLock import time """ Rlock可以被第一个抢到锁的人连续的acquire和release 每acquire一次锁身上的计数加1 每release一次锁身上的计数减1 只要锁的计数不为0 其他人都不能抢 """ mutexA = Lock() mutexB = Lock() 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()
2.递归锁
A与B变成了一把锁,这样只有在一个人抢到锁时,别人就无法抢锁,只有等这个人释放了锁,别人才可以抢锁。
from threading import Thread,Lock,current_thread,RLock import time """ Rlock可以被第一个抢到锁的人连续的acquire和release 每acquire一次锁身上的计数加1 每release一次锁身上的计数减1 只要锁的计数不为0 其他人都不能抢 """ 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()
七、Even
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的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()
import queue q = queue.Queue() q.put('1') q.put('2') q.put('3') print(q.get()) print(q.get()) print(q.get()) 打印结果:1 2 3
2.class queue.LifoQueue()
import queue q = queue.LifoQueue() q.put('1') q.put('2') q.put('3') print(q.get()) print(q.get()) print(q.get()) 打印结果:3 2 1
3.class queue.priorityQueue()
import queue q = queue.PriorityQueue() q.put(5) q.put(-2) q.put(3) print(q.get()) print(q.get()) print(q.get()) 打印结果:-2 3 5