Python守护程序中关闭问题的调试技术

时间:2013-04-19 02:26:26

标签: python multithreading daemon

我正在用Python线程做一些粗略的事情,包括守护进程。

我在某些测试中出现间歇性错误:

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

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

仔细检查我自己的代码并没有帮助,但我对调试的下一步有点不知所措。 我可以使用哪些调试技术来了解更多关于在关机期间可能导致运行时失效的异常情况?

精美印刷:

  • Windows,CPython,2.7.2 - 在Ubuntu上无法重现。
  • 问题大约发生在3%的时间 - 因此可重复,只是不可靠。
  • myconsumerthread中的代码有一个catch-all异常处理程序,它试图将异常的名称写入sys.stderr。 (sys是否已经关闭?)
  • 怀疑问题与快速关闭守护程序线程有关;在他们完全初始化之前。 this区域内的东西,但我没有什么证据 - 当然不足以指出Python错误。
  • 哈,我发现了一种新的症状,标志着我在疯狂中的转折点!
    • 如果我测试工具中的import time (不是实时代码),从不使用,则频率会降至约0.5%。
    • 如果我import turtle在我的测试工具中(我发誓我的生活,我的代码中没有龟图形;我选择这是我可以快速找到的最不相关的库)异常开始被捕获一个不同的线程,它出现在大约第三个的运行中。

1 个答案:

答案 0 :(得分:1)

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

在此之前,如果我的记忆能很好,那么这些就是我关注的领域。

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

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

我发现奇怪的是它没有显示回溯。您可能会尝试注释掉catch-all,然后让Python将其发送到std.error。希望你能够看到你的死亡。

<强>更新
我知道我之前已经看过这个问题了......下面你会找到一个产生错误的例子(实际上很多都是)。请注意,没有其他回溯消息...为了在看到错误消息后的完整性,请取消注释queue.get行并注释掉time.sleep。错误应该消失。 再次重新运行后,错误不会出现......这与您在偶发故障率中看到的内容一致...您可能需要运行几次以查看错误。< /强>

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

话虽如此,我相信在等待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)