Python线程在退出程序时出现异常错误

1 投票
1 回答
4398 浏览
提问于 2025-04-16 06:26

大家好,

我正在用Python 2.4.3和wxPython开发一个图形用户界面(GUI)。一切运行得很好,除了在退出主程序(关闭GUI的主窗口)时。有时候会出现奇怪的错误,有时候又没有。虽然我在Python的邮件列表上找到了相同的错误报告(链接是 http://bugs.python.org/issue1722344),但我不确定我的情况是否和这个一样。我不知道这个问题最终是怎么解决的,也不知道我该怎么做才能克服这个问题。

控制台显示的错误信息如下:

Exception in thread Thread-1 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "/usr/lib/python2.4/threading.py", line 442, in __bootstrap
  File "/opt/company/workspace/username/application/src/mainwidget.py", line 1066, in run
  File "/usr/lib/python2.4/Queue.py", line 89, in put
  File "/usr/lib/python2.4/threading.py", line 237, in notify
exceptions.TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType
Unhandled exception in thread started by 
Error in sys.excepthook:

Original exception was:

以下是我代码的一部分(与线程相关的代码是完整的,我提取了其余部分的主要操作)。当我使用GUI启动一个外部子进程时,同时会创建一个wx.TextCtrl对象。这个wx.TextCtrl对象用于输入和打印外部子进程的输出。

class BashProcessThread(threading.Thread):
    def __init__(self, readlineFunc):
        threading.Thread.__init__(self)
        self.readlineFunc = readlineFunc
        self.lines = []
        self.outputQueue = Queue.Queue()
        self.setDaemon(True)

    def run(self):
        while True:
           line = self.readlineFunc()
           self.outputQueue.put(line)
           if (line==""):
            break
        return ''.join(self.lines)

    def getOutput(self):
        """ called from other thread """            
        while True:
            try:
                line = self.outputQueue.get_nowait()
                lines.append(line)
            except Queue.Empty:
                break
        return ''.join(self.lines)

class ExternalProcWindow(wx.Window):
    def __init__(self, parent, externapp):
        wx.Window.__init__(self, parent, -1, pos=wx.DefaultPosition, size = wx.Size(1200, 120))
        self.externapp=externapp
        self.prompt = externapp.name.lower() + '>>'
        self.textctrl = wx.TextCtrl(self, -1, '', size= wx.Size(1200, 120), style=wx.TE_PROCESS_ENTER|wx.TE_MULTILINE)
        self.default_txt = self.textctrl.GetDefaultStyle()
        self.textctrl.AppendText(self.prompt)
        self.outputThread = BashProcessThread(self.externapp.sub_process.stdout.readline)
        self.outputThread.start()
        self.textctrl.SetFocus()
        self.__bind_events()         
        self.Fit()

    def __bind_events(self):
        self.Bind(wx.EVT_TEXT_ENTER, self.__enter)

    def __enter(self, e):
        nl=self.textctrl.GetNumberOfLines()
        ln =  self.textctrl.GetLineText(nl-1)
        ln = ln[len(self.prompt):]     
        self.externapp.sub_process.stdin.write(ln+"\n")
        time.sleep(.3)
        self.textctrl.AppendText(self.outputThread.getOutput())

class ExternApp:
    def launch(self):
        self.sub_process = subprocess.Popen(launchcmd, stdin=subprocess.PIPE, 
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)

1 个回答

5

这个问题是因为使用了 threading.Thread.setDaemon。被设置为守护线程的线程不会阻止 Python 解释器退出,但它们仍然会继续运行。因为在程序结束之前,Python 会清理环境,所以当一些东西被移除时,这些线程可能会遇到麻烦。这会引发一个异常,而线程类会试图为你打印这个异常,但这时也会失败,因为程序正在退出。

你可以尝试让这个异常不显示,但这有点棘手(而且如果线程做了什么重要的事情,可能会掩盖真正的问题。不过在这里并不是这种情况。)或者你可以在程序退出之前让线程停止,并且不把线程设置为守护线程。或者你也可以干脆不使用线程。我不记得 wxPython 是否有方便的方法来获取进程的输出或者进行异步输入输出,但很多图形用户界面工具包都有。而且还有 Twisted,它可以为你处理所有这些事情。

撰写回答