多线程中一个线程须要一次得到多个锁,怎么才能实现不会出现死锁的状况。 多线程
在多线程程序中,死锁问题很大一部分是因为线程同时获取多个锁形成的。举个例子:一个线程获取了第一个锁,而后在获取第二个锁的 时候发生阻塞,那么这个线程就可能阻塞其余线程的执行,从而致使整个程序假死。 解决死锁问题的一种方案是为程序中的每个锁分配一个惟一的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()
函数来申请锁, 示例以下:ui
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()
死锁是每个多线程程序都会面临的一个问题(就像它是每一本操做系统课本的共同话题同样)。根据经验来说,尽量保证每个 线程只能同时保持一个锁,这样程序就不会被死锁问题所困扰。一旦有线程同时申请多个锁,一切就不可预料了。spa
死锁的检测与恢复是一个几乎没有优雅的解决方案的扩展话题。一个比较经常使用的死锁检测与恢复的方案是引入看门狗计数器。当线程正常 运行的时候会每隔一段时间重置计数器,在没有发生死锁的状况下,一切都正常进行。一旦发生死锁,因为没法重置计数器致使定时器 超时,这时程序会经过重启自身恢复到正常状态。操作系统
避免死锁是另一种解决死锁问题的方式,在进程获取锁的时候会严格按照对象id升序排列获取,通过数学证实,这样保证程序不会进入 死锁状态。避免死锁的主要思想是,单纯地按照对象id递增的顺序加锁不会产生循环依赖,而循环依赖是 死锁的一个必要条件,从而避免程序进入死锁状态。线程