孤叶翔云

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

同步锁又称互斥锁,可以让线程同时访问共享数据,但这也会造成死锁问题。

死锁的产生

两个线程在执行过程中,都在等待对方先释放占用的资源,从而使程序无法继续进行下去,造成假死状态。这就好比两个人进行交易,一个人卖货,一个人出钱,卖货的要求对方先给钱,我才出货;而出钱的要求对方先出货,我才付钱。这样双方僵持,导致交易无法进行。

下面是一个死锁的示例:

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

posted on 2019-03-22 11:04  孤叶翔云  阅读(166)  评论(0编辑  收藏  举报