zoukankan      html  css  js  c++  java
  • 死锁与递归锁

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

    死锁的产生

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

    下面是一个死锁的示例:

    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

  • 相关阅读:
    winform 监视DataGridView的滚动条,加载数据
    SQL 自动生成行号
    SQL 删除重复的数据(多个字段判断),只保留一条数据
    C# 一个简单的 工厂模式 例子
    winform 重写TextBox的OnTextChanged方法(事件)
    winform 给文本框加载内容的时候 始终让文本框的滚动条到底(允许显示多行Multiline=True)
    winform 制定DataGridViewTextColumn列(更改DataGridView的Cell的状态很有用)
    winform 使用委托 实现多线程访问控件
    C# 一个简单的递归函数和 两个List<T> 合并
    ADO.NET  把数据库A的某表的数据内容复制到数据库B的某表内
  • 原文地址:https://www.cnblogs.com/guyexiangyun/p/10576117.html
Copyright © 2011-2022 走看看