
死锁示例
在进行多线程处理时,经常会遇到死锁问题. 在学习操作系统时,您将讨论与死锁相关的事情. 让我们使用Python直观地演示它.
死锁的一个原因是互斥. 假设在银行系统中,用户a尝试将100个块转让给用户b,同时用户b尝试将200个块转让给用户a,则可能发生死锁.
两个线程等待彼此的锁,占用彼此的资源而不释放它们.

#coding=utf-8 import time import threading class Account: def __init__(self, _id, balance, lock): self.id = _id self.balance = balance self.lock = lock def withdraw(self, amount): self.balance -= amount def deposit(self, amount): self.balance += amount def transfer(_from, to, amount): if _from.lock.acquire():#锁住自己的账户 _from.withdraw(amount) time.sleep(1)#让交易时间变长,2个交易线程时间上重叠,有足够时间来产生死锁 print 'wait for lock...' if to.lock.acquire():#锁住对方的账户 to.deposit(amount) to.lock.release() _from.lock.release() print 'finish...' a = Account('a',1000, threading.Lock()) b = Account('b',1000, threading.Lock()) threading.Thread(target = transfer, args = (a, b, 100)).start() threading.Thread(target = transfer, args = (b, a, 200)).start()
防止死锁的锁定机制
问题:
您正在编写一个多线程程序,其中线程需要一次获取多个锁,如何避免此时出现死锁问题.

解决方案:
在多线程程序中多线程死锁的例子,死锁问题的很大一部分是由线程同时获取多个锁引起的. 例如,如果某个线程获取了第一个锁,然后在获取第二个锁的同时进行了阻塞,则该线程可能会阻塞其他线程的执行,从而导致整个程序被挂起. 解决死锁问题的一种方法是为程序中的每个锁分配一个唯一的ID,然后仅允许按升序使用多个锁. 使用上下文管理器很容易实现此规则. 示例如下:
import threading from contextlib import contextmanager # Thread-local state to stored information on locks already acquired _local = threading.local() @contextmanager def acquire(*locks): # Sort locks by object identifier locks = sorted(locks, key=lambda x: id(x)) # Make sure lock order of previously acquired locks is not violated acquired = getattr(_local,'acquired',[]) if acquired and max(id(lock) for lock in acquired) >= id(locks[0]): raise RuntimeError('Lock Order Violation') # Acquire all of the locks acquired.extend(locks) _local.acquired = acquired try: for lock in locks: lock.acquire() yield finally: # Release locks in reverse order of acquisition for lock in reversed(locks): lock.release() del acquired[-len(locks):]
如何使用此上下文管理器?您可以按照常规方式创建锁对象,但是无论是单个锁还是多个锁,都可以使用Acquisition()函数申请该锁. 示例如下:

import threading x_lock = threading.Lock() y_lock = threading.Lock() def thread_1(): while True: with acquire(x_lock, y_lock): print('Thread-1') def thread_2(): while True: with acquire(y_lock, x_lock): print('Thread-2') t1 = threading.Thread(target=thread_1) t1.daemon = True t1.start() t2 = threading.Thread(target=thread_2) t2.daemon = True t2.start()
如果执行此代码,即使它以不同的顺序获得了不同功能的锁,也不会死锁. 关键是在第一段代码中,我们对这些锁进行了排序. 通过排序,无论用户请求锁的顺序如何,都将以固定顺序获取锁. 如果嵌套了多个Acquisition()操作,则可以使用线程本地存储(TLS)来检测潜在的死锁问题. 假设您的代码是这样写的:
import threading x_lock = threading.Lock() y_lock = threading.Lock() def thread_1(): while True: with acquire(x_lock): with acquire(y_lock): print('Thread-1') def thread_2(): while True: with acquire(y_lock): with acquire(x_lock): print('Thread-2') t1 = threading.Thread(target=thread_1) t1.daemon = True t1.start() t2 = threading.Thread(target=thread_2) t2.daemon = True t2.start()
如果运行此版本的代码,将导致线程崩溃,异常消息可能如下所示:

Exception in thread Thread-1: Traceback (most recent call last): File "/usr/local/lib/python3.3/threading.py", line 639, in _bootstrap_inner self.run() File "/usr/local/lib/python3.3/threading.py", line 596, in run self._target(*self._args, **self._kwargs) File "deadlock.py", line 49, in thread_1 with acquire(y_lock): File "/usr/local/lib/python3.3/contextlib.py", line 48, in __enter__ return next(self.gen) File "deadlock.py", line 15, in acquire raise RuntimeError("Lock Order Violation") RuntimeError: Lock Order Violation >>>
崩溃的原因是每个线程都记录它已获取的锁. Acquisition()函数检查以前获取的锁的列表. 由于这些锁是按升序获取的,因此该函数假定以前获取的锁的id必须小于新获取的锁的ID,并触发异常.
讨论
死锁是每个多线程程序都将面临的问题(就像在每个操作系统教科书中都是一个常见主题一样). 根据经验,尽可能确保每个线程只能同时保持一个锁,这样程序就不会受到死锁问题的困扰. 一旦一个线程同时申请了多个锁,一切都是不可预测的.
死锁检测和恢复是扩展的主题,几乎没有优雅的解决方案. 死锁检测和恢复的更常用方案是引入看门狗计数器. 当线程正常运行时,计数器将定期重置,并且一切将正常进行而不会发生死锁. 一旦发生死锁,由于无法重置计数器,计时器将超时. 此时,程序将通过重新启动自身而返回到正常状态.
避免死锁是解决死锁问题的另一种方法. 当进程获取锁时,它将严格按照对象id的升序获取它. 经过数学证明后,这可以确保程序不会进入死锁状态. 证明留给读者作为练习. 避免死锁的主要思想是多线程死锁的例子,简单地以增加对象id的顺序添加锁不会产生循环依赖,而循环依赖是死锁的必要条件,以避免程序进入死锁状态.
以下是有关线程死锁的经典问题: 本节的最后一个示例是“哲学家就餐问题”. 标题是这样的: 五位哲学家围着桌子坐着,每个人面前都有一碗米饭和一根筷子. 在这里,每个哲学家都可以被视为一个独立的线程,每个筷子都可以被视为一个锁. 每个哲学家可以处于以下三种状态之一: 冥想,思考和饮食. 应该注意的是,每个哲学家都需要吃两根筷子,因此出现了一个问题: 如果每个哲学家都拿起左手的筷子,那么他们全部五个只能坐在那里用一根筷子,直到饿死. 此时,他们进入了死锁状态. 以下是避免“死锁问题”机制的死锁避免机制的简单实现:
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-201139-1.html
看过【超级战舰】吗
应该是个不错的选择