一、关于死锁。
死锁,就是当多个进程或者线程在执行的过程中,因争夺共享资源而造成的一种互相等待的现象,一旦产生了死锁,不加人工处理,程序会一直等待下去,这也被称为死锁进程。
下面是一个产生“死锁”现象的例子:
import threading
import time
lock_a = threading.Lock()
lock_b = threading.Lock()
class test_thread(threading.Thread):
def __init__(self):
super(test_thread,self).__init__()
def run(self):
self.fun1()
self.fun2()
def fun1(self):
lock_a.acquire()
print "I am %s , get res: %s---%s" %(self.name, "ResA",time.time())
lock_b.acquire()
print "I am %s , get res: %s---%s" %(self.name, "ResB",time.time())
lock_b.release()
lock_a.release()
def fun2(self):
lock_b.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
time.sleep(0.2)
lock_a.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
lock_a.release()
lock_b.release()
if __name__ == "__main__":
print "start---------------------------%s"%(time.time())
for i in range(0, 10):
my_thread = test_thread()
my_thread.start()
输出执行结果:
start---------------------------1494682814.1
I am Thread-1 , get res: ResA---1494682814.1
I am Thread-1 , get res: ResB---1494682814.1
I am Thread-1 , get res: ResB---1494682814.1
I am Thread-2 , get res: ResA---1494682814.1
下面来分析代码,为什么会产生死锁:
开了10个线程,首先肯定会有一个线程去拿到lock_a这把锁,其余的线程只能阻塞,一直等到lock_a这把锁被释放,然后这个线程又获得了一把锁就是lock_b,执行完了一条print操作后,释放lock_b这把锁,此时,其他的线程还是没有办法去执行func1里面的资源,释放了lock_b这把锁后,紧接着lock_a也被释放了,此时,下一个线程就可以去执行func1中的资源了,接下来,线程1执行到了func2拿到了lock_b这把锁,然后执行一个print输出,I am Thread-1 , get res: ResB---1494682814.1,sleep0.2秒,在第一个线程sleep的过程中第二个线程开始执行,第二个线程在执行func1的时候,首先拿到了lock_a这把锁,执行了下面的print语句,I am Thread-2 , get res: ResA---1494682814.1,此时的情况就是线程1拿到了lock_b这把锁,线程2拿到了lock_a这把锁,那么问题来了,线程2如果想继续执行func1后面的内容,需要去获得lock_b这把锁,而此时lock_b锁已经被线程1拿走了(线程1执行到了func2拿走了lock_b锁,并且还没被释放~),线程1,sleep0.2秒后,需要继续执行func2中的内容,此时,需要拿到lock_a锁,(lock_a锁,被线程2执行func1的时候拿走了,并且没有被释放~),现在的情况就是线程1需要lock_a锁,但是lock_a在线程2手里,线程2需要lock_b锁,但是lock_b锁在线程1手里~双方都没有办法执行到后面的释放操作。
这也就相当于两个人要做交易,甲手里有苹果乙手里有菠萝,甲想吃乙手里的菠萝,乙想吃甲手里的苹果,但是谁都不愿意先把自己手里的东西先给对方~所以程序就会一直卡在这。
这就是死锁形成的原理。
二、递归锁。
解决死锁问题有一个特别有效的方法,就是递归锁.
递归锁与普通的互斥锁最大的不同就是,一个锁的对象内部,维护了一个计数器,这个计数器的初始值是0,当一个线程acquire一次这个锁时,内部计数器+1,但是,这把锁的计数器一旦大于0,其他的线程是无法拿到这把锁的,只有当前线程可以拿。
(当前线程acquire一次,计数器+1,release一次计数器-1,所以,当前的线程想要彻底释放掉递归锁,acquire多少次,就要release多少次!!!)
(这个计数器,就是递归锁中的count。)
还拿刚才的那段产生死锁的代码来举例:
import threading
import time
r_lock = threading.RLock() #RLock是用来产生递归锁的一个类,产生一个递归锁。
class test_thread(threading.Thread):
def __init__(self):
super(test_thread,self).__init__()
def run(self):
self.fun1()
self.fun2()
def fun1(self):
r_lock.acquire() #count+1
print "I am %s , get res: %s---%s" %(self.name, "ResA",time.time())
r_lock.acquire() #count再+1
print "I am %s , get res: %s---%s" %(self.name, "ResB",time.time())
r_lock.release() #count -1
r_lock.release() #count 再-1
def fun2(self):
r_lock.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
time.sleep(0.2)
r_lock.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
r_lock.release()
r_lock.release()
if __name__ == "__main__":
print "start---------------------------%s"%(time.time())
r_lock = threading.RLock()
for i in range(0, 10):
my_thread = test_thread()
my_thread.start()
输出结果:
start---------------------------1494687542.43
I am Thread-1 , get res: ResA---1494687542.43
I am Thread-1 , get res: ResB---1494687542.43
I am Thread-1 , get res: ResB---1494687542.43
I am Thread-1 , get res: ResA---1494687542.63
I am Thread-2 , get res: ResA---1494687542.63
I am Thread-2 , get res: ResB---1494687542.63
I am Thread-2 , get res: ResB---1494687542.63
I am Thread-2 , get res: ResA---1494687542.83
I am Thread-4 , get res: ResA---1494687542.83
I am Thread-4 , get res: ResB---1494687542.83
I am Thread-4 , get res: ResB---1494687542.83
I am Thread-4 , get res: ResA---1494687543.04
I am Thread-6 , get res: ResA---1494687543.04
I am Thread-6 , get res: ResB---1494687543.04
I am Thread-6 , get res: ResB---1494687543.04
I am Thread-6 , get res: ResA---1494687543.24
I am Thread-8 , get res: ResA---1494687543.24
I am Thread-8 , get res: ResB---1494687543.24
I am Thread-8 , get res: ResB---1494687543.24
I am Thread-8 , get res: ResA---1494687543.44
I am Thread-10 , get res: ResA---1494687543.44
I am Thread-10 , get res: ResB---1494687543.44
I am Thread-10 , get res: ResB---1494687543.44
I am Thread-10 , get res: ResA---1494687543.65
I am Thread-5 , get res: ResA---1494687543.65
I am Thread-5 , get res: ResB---1494687543.65
I am Thread-5 , get res: ResB---1494687543.65
I am Thread-5 , get res: ResA---1494687543.85
I am Thread-9 , get res: ResA---1494687543.85
I am Thread-9 , get res: ResB---1494687543.85
I am Thread-9 , get res: ResB---1494687543.85
I am Thread-9 , get res: ResA---1494687544.06
I am Thread-7 , get res: ResA---1494687544.06
I am Thread-7 , get res: ResB---1494687544.06
I am Thread-7 , get res: ResB---1494687544.06
I am Thread-7 , get res: ResA---1494687544.26
I am Thread-3 , get res: ResA---1494687544.26
I am Thread-3 , get res: ResB---1494687544.26
I am Thread-3 , get res: ResB---1494687544.26
I am Thread-3 , get res: ResA---1494687544.46
从上面的例子来看,死锁的问题被完美的解决掉了。
最后,总结下递归锁:
在python中,如果同一个线程需要多次去访问同一个共享资源,这个时候,就可以使用递归锁(RLock),递归锁的内部,维护了一个Lock对象和一个counter计数变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
所以说RLock可以完全代替Lock,能用递归锁尽量用递归锁!
本文转自苏浩智 51CTO博客,原文链接:http://blog.51cto.com/suhaozhi/1925393,如需转载请自行联系原作者