如何在从线程中断main方法时防止try / except / finally中的死锁

时间:2017-05-14 04:46:54

标签: python multithreading python-2.7 concurrency

我在一个应用程序中看到了这个死锁,导致它卡住了。该应用程序有一个主线程以及一个在后台处理一些工作的线程。

两个线程同时使用的服务发生连接错误。主线程捕获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

finallyexcept执行从我在文档中看到的开始,都要求锁定,其中一个获胜,然后应用程序被线程中断,如果发生这种情况在拥有线程释放锁之前,另一个线程等待锁定,应用程序无所事事。

修复将是尝试获取锁定并在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),并检查主应用程序循环中的队列并从那里引发错误,但是这可以添加延迟表达这些错误。

1 个答案:

答案 0 :(得分:0)

据我发现,这个难题在Python中是无法解决的。正是这种情况导致.NET锁被重新设计为包括通过引用传递的布尔参数,并保证在/如果获得锁时被原子设置。然后,对C#using / lock语句(类似于Context Managers)进行了重新设计以利用这种模式。

https://msdn.microsoft.com/en-us/library/dd289498.aspx