【问题标题】:How to prevent deadlock in try/except/finally while interrupting the main method from a thread如何在从线程中断主方法时防止 try/except/finally 中的死锁
【发布时间】: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

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

解决方法是在 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


【解决方案1】:

据我所知,这个困境在 Python 中是无法解决的。这正是导致 .NET 锁被重新设计以包含通过引用传递的布尔参数并保证在获得锁时/如果获得锁时自动设置的情况。 C# using/lock 语句(类似于上下文管理器)随后被重新设计以利用此模式。

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

【讨论】:

    猜你喜欢
    • 2018-03-19
    • 1970-01-01
    • 2019-08-14
    • 1970-01-01
    • 2022-06-12
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多