同步锁又称互斥锁,可以让线程同时访问共享数据,但这也会造成死锁问题。
死锁的产生
两个线程在执行过程中,都在等待对方先释放占用的资源,从而使程序无法继续进行下去,造成假死状态。这就好比两个人进行交易,一个人卖货,一个人出钱,卖货的要求对方先给钱,我才出货;而出钱的要求对方先出货,我才付钱。这样双方僵持,导致交易无法进行。
下面是一个死锁的示例:
from threading import Thread, Lock import time class MyThread(Thread): def run(self): self.actionA() self.actionB() def actionA(self): lock_A.acquire() # 获取A锁 print("线程--%s--拿到了A锁... at %s" % (self.name,time.ctime())) time.sleep(3) lock_B.acquire() # 获取B锁 print("线程--%s--拿到了B锁... at %s" % (self.name,time.ctime())) lock_B.release() # 释放B锁 print("线程--%s--释放了B锁... at %s" % (self.name,time.ctime())) lock_A.release() # 释放A锁 print("线程--%s--释放了A锁... at %s" % (self.name,time.ctime())) def actionB(self): lock_B.acquire() print("线程--%s--拿到了B锁... at %s" % (self.name,time.ctime())) lock_A.acquire() print("线程--%s--拿到了A锁... at %s" % (self.name,time.ctime())) lock_A.release() print("线程--%s--释放了A锁... at %s" % (self.name,time.ctime())) lock_B.release() print("线程--%s--释放了B锁... at %s" % (self.name,time.ctime())) if __name__ == '__main__': lock_A = Lock() lock_B = Lock() for i in range(5): t = MyThread() t.start()
运行时,可以看到程序卡住不动了:
线程--Thread-1--拿到了A锁... at Fri Mar 22 09:42:09 2019 线程--Thread-1--拿到了B锁... at Fri Mar 22 09:42:12 2019 线程--Thread-1--释放了B锁... at Fri Mar 22 09:42:12 2019 线程--Thread-1--释放了A锁... at Fri Mar 22 09:42:12 2019 线程--Thread-1--拿到了B锁... at Fri Mar 22 09:42:12 2019 线程--Thread-2--拿到了A锁... at Fri Mar 22 09:42:12 2019
死锁的产生如下所示:
两个线程在分别等待对方所持有的锁,造成程序无法执行下去,出现假死的状态。
递归锁
python中提供了一种“递归锁”,可以来解决死锁问题。示例如下所示:
1 from threading import Thread, Lock, _RLock 2 import time 3 4 class MyThread(Thread): 5 6 def run(self): 7 self.actionA() 8 self.actionB() 9 10 def actionA(self): 11 12 rlock.acquire() 13 print("线程--%s, rlock被获取-%s-次" % (self.name,rlock._count)) # _RLock中的计数器属性_count,该属性记录rlock被获取几次。 14 15 time.sleep(3) 16 17 rlock.acquire() 18 print("线程--%s, rlock被获取-%s-次" % (self.name,rlock._count)) 19 20 rlock.release() 21 22 rlock.release() 23 24 25 def actionB(self): 26 27 rlock.acquire() 28 print("线程--%s, rlock被获取-%s-次" % (self.name,rlock._count)) 29 rlock.acquire() 30 print("线程--%s, rlock被获取-%s-次" % (self.name,rlock._count)) 31 32 rlock.release() 33 rlock.release() 34 35 36 if __name__ == '__main__': 37 rlock = _RLock() # 创建一个递归锁实例,threading中还定义了一个内置方法RLock(),该方法返回一个_RLock对象 38 # rlock = RLock() # 也可以用这个方法来创建递归锁的实例 39 for i in range(5): 40 t = MyThread() 41 t.start()
运行结果如下所示:
线程--Thread-1, rlock被获取-1-次 线程--Thread-1, rlock被获取-2-次 线程--Thread-2, rlock被获取-1-次 线程--Thread-2, rlock被获取-2-次 线程--Thread-3, rlock被获取-1-次 线程--Thread-3, rlock被获取-2-次 线程--Thread-4, rlock被获取-1-次 线程--Thread-4, rlock被获取-2-次 线程--Thread-5, rlock被获取-1-次 线程--Thread-5, rlock被获取-2-次 线程--Thread-1, rlock被获取-1-次 线程--Thread-1, rlock被获取-2-次 线程--Thread-2, rlock被获取-1-次 线程--Thread-2, rlock被获取-2-次 线程--Thread-3, rlock被获取-1-次 线程--Thread-3, rlock被获取-2-次 线程--Thread-4, rlock被获取-1-次 线程--Thread-4, rlock被获取-2-次 线程--Thread-5, rlock被获取-1-次 线程--Thread-5, rlock被获取-2-次 ***Repl Closed***
可以看到,每个线程都成功地执行完毕。_RLock中定义了计数器_count属性,每次_RLock对象被请求时,该属性会自加1;再次被请求时,它会先判断请求者(请求线程)是否与自己当前线程一致(即查看_owner属性),如果一致,那么_count会继续自加1,如果不一致就会拒绝其请求。只要_count属性大于0,该rlock实例就不会被其他请求线程获取。
例如:线程A执行rlock.acquire(),那么rlock._owner就被设置为线程A,rlock._count就自加1(等于1);如果线程A再次执行rlock.acquire(),那么rlock._count再次自加1(等于2);此时,如果线程B执行rlock.acquire(),那么首先判断与rlock的_owner属性不一致,再判断_count大于0,此时就拒绝线程B的请求,阻塞其执行。
参考博客:https://blog.csdn.net/u013210620/article/details/78723704
参考博客:http://www.cnblogs.com/yuanchenqi/articles/6248025.html