day9-递归锁(RLock)
产生背景
两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。看下面的例子:
import threading,time def run1(): print("grab the first part data") lock.acquire() #加锁(第一个小门) global num num += 1 lock.release() #释放锁(第一个小门) return num def run2(): print("grab the second part data") lock.acquire() #加锁(第二个小门) global num2 num2 += 1 lock.release() #释放锁(第二个小门) return num2 def run3(): lock.acquire() #加锁(第一个大门) res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() #释放锁(第一个大门) print(res, res2) num, num2 = 0, 0 lock = threading.Lock() for i in range(10): #生成10个线程,每个线程都启动run3函数 t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: #等所有线程执行完毕(最终只有一个主线程) 相当于for t in t_obj:t.join() print(threading.active_count()) else: print('----all threads done---') print(num, num2) #输出 11 11 11 11 11 11 。 。 。 死循环
解析:按照程序自上而下执行,2个小门中的锁在同一时间应该只有一把加锁,但是在程序执行后,出现了死循环,这是为什么呢?同时我们对程序进行修改将run3中的res2=run2注释掉,同样会出现死循环。打印出11是程序运行10个线程都没有退出锁+主线程要等待子线程结束,所以总数为11,处于卡死状态。
因为run3函数和run1函数都获取了同一个锁,而run3函数又会调用run1函数。如果lock锁是个非递归锁,则这个程序就会立即死锁。因此在为一段程序加锁时要格外小心,否则很容易因为这种调用关系而造成死锁。
解决方案:这种情况下,就可以使用递归锁。
递归锁
说明:在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁,这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release。其他的线程才能获取资源。
#非递归锁 lock = threading.Lock() --------------------程序修改为--------------------------------------- for i in range(2): #生成2个线程,每个线程都启动run3函数(便于分析结果) #递归锁 lock = threading.RLock() #生成递归锁实例 #程序运行递归锁输出 grab the first part data --------between run1 and run2----- grab the second part data 1 1 grab the first part data --------between run1 and run2----- grab the second part data 2 2 ----all threads done--- 2 2 Process finished with exit code 0