我在一个应用程序中看到了这个死锁,导致它卡住了。该应用程序有一个主线程以及一个在后台处理一些工作的线程。
两个线程同时使用的服务发生连接错误。主线程捕获except Exception
的异常,后台线程捕获错误并在主应用程序发生时中断它们。 python日志记录模块中发生死锁,同时获取锁定以将写入序列化到标准输出。下面是死锁的再现(没有记录模数,但模仿其行为)
import thread
from threading import Thread, RLock
lock = RLock()
def log(msg):
lock.acquire()
try:
print msg
finally:
lock.release()
def run():
thread.interrupt_main()
Thread(target=run).start()
try:
1 / 0
except KeyboardInterrupt:
log('keyboardinterrupt')
except Exception, e:
log('Exception')
raise
finally:
log('finally')
在循环中运行它,最终会发生死锁。
while true; do python deadlock.py; done
finally
和except
执行从我在文档中看到的开始,都要求锁定,其中一个获胜,然后应用程序被线程中断,如果发生这种情况在拥有线程释放锁之前,另一个线程等待锁定,应用程序无所事事。
修复将是尝试获取锁定并在finally中释放它,但它是在python code中的try之外获取的。我认为这是有意义的,因为它应该阻止,直到它获得锁定或失败。
我可以在我的应用程序中执行以下操作:
try:
log.info('msg')
finally:
for handler in log.handlers: # (and its parents' handlers)
try:
handler.lock.release()
except: # ignore if it was not acquired
pass
但这看起来不对,我想知道这里是否有更好的解决方案?
我想过在线程中发生错误而没有中断主线程而是使用错误队列(类似于go),并检查主应用程序循环中的队列并从那里引发错误,但是这可以添加延迟表达这些错误。
答案 0 :(得分:0)
据我发现,这个难题在Python中是无法解决的。正是这种情况导致.NET锁被重新设计为包括通过引用传递的布尔参数,并保证在/如果获得锁时被原子设置。然后,对C#using / lock语句(类似于Context Managers)进行了重新设计以利用这种模式。