锁:
1.同步锁
需求:对一个全局变量,开启100个线程,每个线程都对该全局变量做减1操作;
不加锁,代码如下:
import time import threading num = 100 #设定一个共享变量 def addNum(): global num #在每个线程中都获取这个全局变量 #num-=1 temp=num time.sleep(0.1) num =temp-1 # 对此公共变量进行-1操作 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待所有线程执行完毕 t.join() print('Result: ', num)
分析:以上程序开启100线程并不能把全局变量
num
减为0,第一个线程执行addNum
遇到I/O阻塞后迅速切换到下一个线程执行addNum
,由于CPU执行切换的速度非常快,在0.1秒内就切换完成了,这就造成了第一个线程在拿到num变量后,在time.sleep(0.1)
时,其他的线程也都拿到了num变量,所有线程拿到的num值都是100,所以最后减1操作后,就是99。加锁实现。加锁,代码如下:
import time import threading num = 100 #设定一个共享变量 def addNum(): with lock: global num temp = num time.sleep(0.1) num = temp-1 #对此公共变量进行-1操作 thread_list = [] if __name__ == '__main__': lock = threading.Lock() #由于同一个进程内的线程共享此进程的资源,所以不需要给每个线程传这把锁就可以直接用。 for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待所有线程执行完毕 t.join() print("result: ",num)
加锁后,第一个线程拿到锁后开始操作,第二个线程必须等待第一个线程操作完成后将锁释放后,再与其它线程竞争锁,拿到锁的线程才有权操作。这样就保障了数据的安全,但是拖慢了执行速度。
注意:
注意:
with lock
是lock.acquire()
(加锁)与lock.release()
(释放锁)的简写。import threading R=threading.Lock() R.acquire() ''' 对公共数据的操作 ''' R.release()
GIL vs Lock
疑问:
机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock?
首先我们需要达成共识:锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据
然后,我们可以得出结论:保护不同的数据就应该加不同的锁。
最后,问题就很明朗了,GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock
详细的:
因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题, 这可以说是Python早期版本的遗留问题。
2. 死锁与递归锁
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态,或系统产生了死锁。这此永远在互相等待的进程称死锁进程。
from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('