python线程锁的理解
(粗浅理解,若有不对请多多指正)
GIL(解释器全局锁)
cpython在设计的时候,为了线程安全设计了GIL,而由于GIL的原因,真正能同时运行的线程只有一个。
但是GIL并不是等线程执行完后才释放,在一个线程中执行了一定行数的字节码或者一定时间片后会自动释放,因此,在多线程大量计算的时候也会出现线程安全问题。
多线程中同时对一个全局变量运算的时候,可以简单的分为以下步骤(字节码行):
- 获取全局变量
- 对全局变量的值进行运算
- 将计算的新值重新赋给全局变量
如果一个线程在拿到全局变量后未完成最后的赋值的时候GIL就释放,另一个线程又拿到全局变量(脏值)就会造成运算错误。
Lock(互斥锁)
为了防止GIL在不应该释放的时候自动释放。
互斥锁容易造成死锁:在同一个线程中,互斥锁没有释放的情况下再次获取(第二次永远获取不到,因为锁还没释放)。
RLock(逻辑锁)(可重复获取的锁)
为了解决互斥锁在开发中容易造成死锁。
逻辑锁可以重复获取,但必须有相同次数的释放。
Condition(条件锁)
获取到条件锁后、释放条件锁前,可以调用wait()方法使该线程沉睡,等待notify()或notify_all()方法将其唤醒。
条件锁可以控制一个线程什么时候沉睡(死锁),什么时候唤醒(将死锁解除),可以更细粒度的控制多个线程的运行状态。
Condition底层实现的时候有2把锁,上层默认情况为逻辑锁(RLock),下层为互斥锁(Lock):
- 获取Condition锁的时候就是获取上层的逻辑锁;
- 一个线程在调用wait()方法的时候有4步操作:①会产生一个下层的互斥锁并获取,②将该互斥锁存放到一个dqueue中,③释放上层的逻辑锁,④再获取一次下层互斥锁,形成下层死锁,即线程沉睡;
- 在另一个线程调用notify()方法时,会将dqueue中的互斥锁取出、释放(解除死锁)并删除,上面线程被唤醒。
Semaphore(信号量)
信号量是Condition锁的一种简单运用,可以实现对线程并发量的控制:
- Semaphore创建实例的时候给定一个并发量参数value(int);
- Semaphore实例中有一个属性:self._cond ----Condition锁的实例;
- Semaphore实例每次调用acquire()方法,内部先调用self._cond.acquire()方法,然后判断value是否=0,如果不等于0,value-=1,如果=0就调用self.__cond.wait()方法使该线程沉睡;
- Semaphore实例每次调用release()方法,内部会将value+=1,并调用self._cond.notify()方法唤醒一个线程。