1

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

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

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

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

仔细检查我自己的代码并没有帮助,但我对调试的下一步有点茫然。我可以使用哪些调试技术来了解更多关于关闭期间哪些异常可能会降低运行时的信息?

印刷精美:

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

1 回答 1

1

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

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

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

在深入挖掘之后,我看到出现与 Queue 相关的相同类型的错误,请参见此处的评论

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

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

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

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)
于 2013-04-19T03:46:58.607 回答