【问题标题】:Debugging techniques for shut-down problems in Python daemonsPython 守护进程关闭问题的调试技术
【发布时间】:2013-04-21 00:09:02
【问题描述】:

我正在用 Python 线程(包括守护程序)做一些棘手的事情。

我在某些测试中遇到间歇性错误:

Exception in thread myconsumerthread (most likely raised during interpreter shutdown):

请注意,没有提供堆栈跟踪/异常详细信息。

仔细检查我自己的代码并没有帮助,但我对调试的下一步有点茫然。 我可以使用哪些调试技术来详细了解关闭期间哪些异常可能导致运行时中断?

细则:

  • Windows、CPython、2.7.2 - 无法在 Ubuntu 上重现。
  • 大约 3% 的时间会出现问题 - 重现性好,只是不可靠。
  • myconsumerthread 中的代码有一个包罗万象的异常处理程序,它尝试将异常名称写入sys.stderr。 (sys 会不会已经被关闭了?)
  • 我怀疑这个问题与关闭守护线程非常快有关;在他们完全初始化之前。 this 区域中的某些内容,但我几乎没有证据 - 肯定不足以指向 Python 错误。
  • 哈,我发现了一种新症状,标志着我陷入精神错乱的转折点!
    • 如果我在我的测试工具(不是实时代码)中import time,并且从不使用它,频率会下降到大约 0.5%。
    • 如果我在我的测试工具中import turtle(我以我的生命发誓,我的代码中没有海龟图形;我选择了这个作为我能快速找到的最不相关的库)异常开始在不同的线程,并且它发生在大约三分之一的运行中。

【问题讨论】:

标签: python multithreading daemon


【解决方案1】:

我曾多次遇到同样的错误。我正在尝试查找/生成一个显示确切消息的示例。

在那之前,如果我没记错的话,这些都是我关注的领域。

  • 正在寻找在守护线程之外删除或关闭的端口、文件、队列等。
  • 检查守护线程中的阻塞调用。即Queue.get(block=True)、pyserial.read() - 超时=无

在深入挖掘之后,我看到与 Queue 的 see comments here 相关的相同类型的错误弹出。

我觉得奇怪的是它不显示回溯。您可能会尝试注释掉 catch-all except 并让 Python 将其发送到 std.error。希望那时你能够看到你正在死去的东西。

更新
我知道我以前见过这个问题......下面你会找到一个产生这个错误的例子(实际上有很多)。请注意,也没有其他回溯消息...为了完整起见,在您看到错误消息后,请取消注释 queue.get 行并注释掉 time.sleeps。错误应该消失。 再次重新运行后,错误不再出现...这与您在零星故障率中看到的一致...您可能需要运行几次才能看到错误。

如果get()和read()等阻塞IO没有提供超时方法,我通常使用time.sleep(x)来限制线程或没有阻塞调用可用(用户界面刷新例如)。

话虽如此,我相信在等待time.sleep() 调用时线程被关闭是有问题的。我相信这个电话每次都让我感到困惑,但我不知道在sleep 方法中究竟是什么导致了它。据我所知,还有其他阻塞调用会显示相同的行为。

import time
import Queue
from threading import Thread

SLAVE_CNT = 50
OWNER_CNT = 10
MASTER_CNT = 2

class ThreadHungry(object):
    def __init__(self):
        self.rx_queue = Queue.Queue()

    def start(self):
        print "Adding Masters..."
        for x in range(MASTER_CNT):
            self.owners = []
            print "Starting slave owners..."
            for y in range(OWNER_CNT):
                owner = Thread(target=self.__owner_action)
                owner.daemon = True
                owner.start()
                self.owners.append(owner)

    def __owner_action(self):
        self.slaves = []
        print "\tStarting slaves..."
        for x in range(SLAVE_CNT):
            slave = Thread(target=self.__slave_action)
            slave.daemon = True
            slave.start()
            self.slaves.append(slave)

        while(1):
            time.sleep(1)
            #self.rx_queue.get(block=True)

    def __slave_action(self):
        while(1):
            time.sleep(1)
            #self.rx_queue.get(block=True)


if __name__ == "__main__":
    c = ThreadHungry()
    c.start()

    # Stop the threads abruptly after 5 seconds
    time.sleep(5)

【讨论】:

  • 我正在尝试跟进您的建议。注释掉包罗万象,它没有任何区别。不使用端口。不使用文件(尽管 stderr 可能算在内)。我的代码中心有一个队列,所以这可能会有所帮助。
  • @Oddthinking 您可能会考虑在您的异常处理程序 (import pdb; pdb.set_trace()) 中使用 Python 调试器来探索。尽管我怀疑它是否会起作用,因为它在关闭事件中死亡。仍然值得一试。
  • 在调试器中手动运行了 100 次。没看到问题。感到无聊,继续前进。
  • 我应该提到我跟进了队列问题 - 这是多线程模块 2.7.4 中修复的 multiprocessing.Queue 问题。但是,我正在使用(多线程)Queue.Queue,所以它看起来不相关。
猜你喜欢
  • 2012-07-28
  • 1970-01-01
  • 1970-01-01
  • 2016-04-29
  • 2011-09-26
  • 2014-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多