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):]
避免死锁是另外一种解决死锁问题的方式,在进程获取锁的时候会严格按照对象 id 升序排列获取,经过数学证明,这样保证程序不会进入死锁状态。
避免死锁的主要思想是,单纯地按照对象 id 递增的顺序加锁不会产生循环依赖,而循环依赖是死锁的一个必要条件,从而避免程序进入死锁状态。
下面是一个简单的使用死锁避免机制解决“哲学家就餐问题”的实现:
import threading
def philosopher(left, right):
while True:
with acquire(left,right):
print(threading.currentThread(), 'eating')
NSTICKS = 5
chopsticks = [threading.Lock() for n in range(NSTICKS)]
for n in range(NSTICKS):
t = threading.Thread(target=philosopher,
args=(chopsticks[n],chopsticks[(n+1) % NSTICKS]))
t.start()