python中threading产生死锁_在Python中形成死锁并防止死锁的例子

6b6dc9714b53609b68bc70514d364282.png

死锁示例

在进行多线程处理时,经常会遇到死锁问题. 在学习操作系统时,您将讨论与死锁相关的事情. 让我们使用Python直观地演示它.

死锁的一个原因是互斥. 假设在银行系统中,用户a尝试将100个块转让给用户b,同时用户b尝试将200个块转让给用户a,则可能发生死锁.

两个线程等待彼此的锁,占用彼此的资源而不释放它们.

569d905548c8e327f7371ecf008f0168.png

#coding=utf-8

importtimeimportthreadingclassAccount:def __init__(self, _id, balance, lock):

self.id=_id

self.balance=balance

self.lock=lockdefwithdraw(self, amount):

self.balance-=amountdefdeposit(self, amount):

self.balance+=amountdeftransfer(_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()

防止死锁的锁定机制

问题:

您正在编写一个多线程程序,其中线程需要一次获取多个锁,如何避免此时出现死锁问题.

031e4832a8ed0cdc2f7a0b00268a343e.png

解决方案:

在多线程程序中多线程死锁的例子,死锁问题的很大一部分是由线程同时获取多个锁引起的. 例如,如果某个线程获取了第一个锁,然后在获取第二个锁的同时进行了阻塞,则该线程可能会阻塞其他线程的执行,从而导致整个程序被挂起. 解决死锁问题的一种方法是为程序中的每个锁分配一个唯一的ID,然后仅允许按升序使用多个锁. 使用上下文管理器很容易实现此规则. 示例如下:

importthreadingfrom contextlib importcontextmanager#Thread-local state to stored information on locks already acquired

_local =threading.local()

@contextmanagerdef acquire(*locks):#Sort locks by object identifier

locks = sorted(locks, key=lambdax: 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=acquiredtry:for lock inlocks:

lock.acquire()yield

finally:#Release locks in reverse order of acquisition

for lock inreversed(locks):

lock.release()del acquired[-len(locks):]

如何使用此上下文管理器?您可以按照常规方式创建锁对象,但是无论是单个锁还是多个锁,都可以使用Acquisition()函数申请该锁. 示例如下:

563dd5bb7dedda4856edf0bdd119a0b5.png

importthreading

x_lock=threading.Lock()

y_lock=threading.Lock()defthread_1():whileTrue:

with acquire(x_lock, y_lock):print('Thread-1')defthread_2():whileTrue:

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)来检测潜在的死锁问题. 假设您的代码是这样写的:

importthreading

x_lock=threading.Lock()

y_lock=threading.Lock()defthread_1():whileTrue:

with acquire(x_lock):

with acquire(y_lock):print('Thread-1')defthread_2():whileTrue:

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()

如果运行此版本的代码,将导致线程崩溃,异常消息可能如下所示:

274e4f429b2c858aae156d6f059ec427.gif

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, inrun

self._target(*self._args, **self._kwargs)

File"deadlock.py", line 49, inthread_1

with acquire(y_lock):

File"/usr/local/lib/python3.3/contextlib.py", line 48, in __enter__

returnnext(self.gen)

File"deadlock.py", line 15, inacquireraise RuntimeError("Lock Order Violation")

RuntimeError: Lock Order Violation>>>

崩溃的原因是每个线程都记录它已获取的锁. Acquisition()函数检查以前获取的锁的列表. 由于这些锁是按升序获取的,因此该函数假定以前获取的锁的id必须小于新获取的锁的ID,并触发异常.

讨论

死锁是每个多线程程序都将面临的问题(就像在每个操作系统教科书中都是一个常见主题一样). 根据经验,尽可能确保每个线程只能同时保持一个锁,这样程序就不会受到死锁问题的困扰. 一旦一个线程同时申请了多个锁,一切都是不可预测的.

死锁检测和恢复是扩展的主题,几乎没有优雅的解决方案. 死锁检测和恢复的更常用方案是引入看门狗计数器. 当线程正常运行时,计数器将定期重置,并且一切将正常进行而不会发生死锁. 一旦发生死锁,由于无法重置计数器,计时器将超时. 此时,程序将通过重新启动自身而返回到正常状态.

避免死锁是解决死锁问题的另一种方法. 当进程获取锁时,它将严格按照对象id的升序获取它. 经过数学证明后,这可以确保程序不会进入死锁状态. 证明留给读者作为练习. 避免死锁的主要思想是多线程死锁的例子,简单地以增加对象id的顺序添加锁不会产生循环依赖,而循环依赖是死锁的必要条件,以避免程序进入死锁状态.

以下是有关线程死锁的经典问题: 本节的最后一个示例是“哲学家就餐问题”. 标题是这样的: 五位哲学家围着桌子坐着,每个人面前都有一碗米饭和一根筷子. 在这里,每个哲学家都可以被视为一个独立的线程,每个筷子都可以被视为一个锁. 每个哲学家可以处于以下三种状态之一: 冥想,思考和饮食. 应该注意的是,每个哲学家都需要吃两根筷子,因此出现了一个问题: 如果每个哲学家都拿起左手的筷子,那么他们全部五个只能坐在那里用一根筷子,直到饿死. 此时,他们进入了死锁状态. 以下是避免“死锁问题”机制的死锁避免机制的简单实现:

本文来自电脑杂谈,转载请注明本文网址:

http://www.pc-fly.com/a/jisuanjixue/article-201139-1.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值