【发布时间】:2017-10-13 02:47:00
【问题描述】:
我在一个应用程序中看到了这个死锁,导致它卡住了。该应用程序有一个主线程以及一个在后台处理一些工作的线程。
两个线程同时使用的服务发生连接错误。主线程使用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 执行从我在文档中看到的一起开始,两者都要求锁定,其中一个获胜,然后应用程序被线程中断,如果这发生在拥有线程之前释放锁,其他线程等待锁,应用程序卡在无所事事。
解决方法是在 try 中获取锁并在 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),并在主应用循环中检查队列并从那里引发错误,但这可以添加延迟出现这些错误。
【问题讨论】:
-
我目前正在处理与您使用两个独立进程类似的设置,但不知道是否是您所追求的(处理部分太重而无法作为线程运行)。如果主要部分发生错误,它只会被捕获并写入文件。但是,如果发生在后台线程中,文本会通过
Queue发回,如果主线程检测到已发回异常,则会将该文本写入文件并停止脚本。 -
是的,这是我正在考虑的一种解决方案,并将其列在问题中。似乎是一个很好的解决方案,但会增加一点延迟。
-
哦,刚刚注意到那部分哈哈,有点略读它,因为我没有正确回答:P 延迟有那么重要吗?
标签: python multithreading python-2.7 concurrency