• 防止死锁的加锁机制


    问题:  

    多线程中一个线程需要一次获得多个锁,怎么才能实现不会出现死锁的情况。  

    解决方案:

    在多线程程序中,死锁问题很大一部分是由于线程同时获取多个锁造成的。举个例子:一个线程获取了第一个锁,然后在获取第二个锁的 时候发生阻塞,那么这个线程就可能阻塞其他线程的执行,从而导致整个程序假死。 解决死锁问题的一种方案是为程序中的每一个锁分配一个唯一的id,然后只允许按照升序规则来使用多个锁,这个规则使用上下文管理器 是非常容易实现的,示例如下:

     1 import threading
     2 from contextlib import contextmanager
     3 
     4 _local = threading.local()  # 获取一个_local对象
     5 
     6 @contextmanager
     7 def acquire(*locks):
     8     locks = sorted(locks, key=lambda x:id(x))  # 把传入的锁排个序
     9 
    10     acquired = getattr(_local, "acquired", [])  # 看一下当前线程有没有acquired属性,没有就返回一个列表
    11     if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):  # 如果不是空列表就有这个属性,
    12         # 如果当前线程的acquired列表中最大的锁比传入的最小的锁要大的话,说明传入的锁中有已经被acquire的锁了,不能再进行acquire
    13         raise RuntimeError("Lock Order Violation")
    14 
    15     acquired.extemd(locks)  # 如果没有出现已经被acquire的锁,我们就把这些锁和当前线程已经有的锁放在一起
    16     _local.acquired = acquired
    17 
    18     try:
    19         for lock in locks:  # 然后按照顺序依次地acquire锁
    20             lock.acquire()
    21         yield
    22 
    23     finally:
    24         for lock in reversed(locks):  # 上下文结束之后按照上锁的顺序的逆序进行解锁
    25             lock.release()
    26         del acquired[-len(locks):]  # 然后删除掉这些lock

    使用方法,可以按照正常途径创建一个锁对象,但不论是单个锁还是多个锁中都使用 acquire() 函数来申请锁, 示例如下:

     1 import threading
     2 x_lock = threading.Lock()
     3 y_lock = threading.Lock()
     4 
     5 def thread_1():
     6     while True:
     7         with acquire(x_lock, y_lock):
     8             print('Thread-1')
     9 
    10 def thread_2():
    11     while True:
    12         with acquire(y_lock, x_lock):
    13             print('Thread-2')
    14 
    15 t1 = threading.Thread(target=thread_1)
    16 t1.daemon = True
    17 t1.start()
    18 
    19 t2 = threading.Thread(target=thread_2)
    20 t2.daemon = True
    21 t2.start()

    讨论

    死锁是每一个多线程程序都会面临的一个问题(就像它是每一本操作系统课本的共同话题一样)。根据经验来讲,尽可能保证每一个 线程只能同时保持一个锁,这样程序就不会被死锁问题所困扰。一旦有线程同时申请多个锁,一切就不可预料了。

    死锁的检测与恢复是一个几乎没有优雅的解决方案的扩展话题。一个比较常用的死锁检测与恢复的方案是引入看门狗计数器。当线程正常 运行的时候会每隔一段时间重置计数器,在没有发生死锁的情况下,一切都正常进行。一旦发生死锁,由于无法重置计数器导致定时器 超时,这时程序会通过重启自身恢复到正常状态。

    避免死锁是另外一种解决死锁问题的方式,在进程获取锁的时候会严格按照对象id升序排列获取,经过数学证明,这样保证程序不会进入 死锁状态。避免死锁的主要思想是,单纯地按照对象id递增的顺序加锁不会产生循环依赖,而循环依赖是 死锁的一个必要条件,从而避免程序进入死锁状态。

  • 相关阅读:
    使用定时器实现获取手机验证码倒计时
    搜索历史管理
    利用vue和jQuery实现中国主要城市搜索与选择
    使用vue、jQuery生成带有logo的二维码
    使用vue-cli脚手架搭建Vue项目
    postcss-px-to-viewport
    git命令操作篇
    小程序中live-player
    对于常用数组的方法总结
    css的加载中动画
  • 原文地址:https://www.cnblogs.com/duanming/p/11841457.html
Copyright © 2020-2023  润新知