一、死锁

简单来说,死锁是一个资源被多次调用,而多次调用方都未能释放该资源就会造成死锁,这里结合例子说明下两种常见的死锁情况。

1、迭代死锁

该情况是一个线程“迭代”请求同一个资源,直接就会造成死锁:

 1import threading
 2import time
 3class MyThread(threading.Thread):
 4    def run(self):
 5        global num
 6        time.sleep(1)
 7        if mutex.acquire(1):
 8            num = num+1
 9            msg = self.name+' set num to '+str(num)
10            print msg
11            mutex.acquire()
12            mutex.release()
13            mutex.release()
14num = 0
15mutex = threading.Lock()
16def test():
17    for i in range(5):
18        t = MyThread()
19        t.start()
20if __name__ == '__main__':
21    test()

上例中,在run函数的if判断中第一次请求资源,请求后还未 release ,再次acquire,最终无法释放,造成死锁。这里例子中通过将print下面的两行注释掉就可以正常执行了 ,除此之外也可以通过可重入锁解决,后面会提到。

2、互相调用死锁

上例中的死锁是在同一个def函数内多次调用造成的,另一种情况是两个函数中都会调用相同的资源,互相等待对方结束的情况。如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

 1import threading
 2import time
 3class MyThread(threading.Thread):
 4    def do1(self):
 5        global resA, resB
 6        if mutexA.acquire():
 7             msg = self.name+' got resA'
 8             print msg
 9             if mutexB.acquire(1):
10                 msg = self.name+' got resB'
11                 print msg
12                 mutexB.release()
13             mutexA.release()
14    def do2(self):
15        global resA, resB
16        if mutexB.acquire():
17             msg = self.name+' got resB'
18             print msg
19             if mutexA.acquire(1):
20                 msg = self.name+' got resA'
21                 print msg
22                 mutexA.release()
23             mutexB.release()
24    def run(self):
25        self.do1()
26        self.do2()
27resA = 0
28resB = 0
29mutexA = threading.Lock()
30mutexB = threading.Lock()
31def test():
32    for i in range(5):
33        t = MyThread()
34        t.start()
35if __name__ == '__main__':
36    test()

这个死锁的示例稍微有点复杂。具体可以理下。

二、可重入锁

为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。这里以例1为例,如果使用RLock代替Lock,则不会发生死锁:

 1import threading
 2import time
 3class MyThread(threading.Thread):
 4    def run(self):
 5        global num
 6        time.sleep(1)
 7        if mutex.acquire(1):
 8            num = num+1
 9            msg = self.name+' set num to '+str(num)
10            print msg
11            mutex.acquire()
12            mutex.release()
13            mutex.release()
14num = 0
15mutex = threading.RLock()
16def test():
17    for i in range(5):
18        t = MyThread()
19        t.start()
20if __name__ == '__main__':
21    test()

和例1不同之处在于threading.Lock()换成了threading.RLock() 。