wxPython的OnExit() 不会停止线程?

2 投票
2 回答
3887 浏览
提问于 2025-04-17 18:54

我正在尝试创建一个线程,并在关闭wxPython应用程序的窗口时结束这个线程。以下是我的代码:

#! /usr/bin/env python

import time, wx
from threading import Thread

class UpdateThread(Thread):
    def __init__(self):
        self.stopped = False
        Thread.__init__(self)
    def run(self):
        while not self.stopped:
            self.updateExchange()
            time.sleep(1)
    def updateExchange(self):
        print("Updated...")

class tradeWindow(wx.Frame):
    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, "Exchange", size = (500, 190))
        panel = wx.Panel(self)
    def OnExit(self):
        tickThread.stopped # I've also tried: tickThread.stopped = True

tickThread = UpdateThread()
tickThread.start()
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = tradeWindow(parent = None, id = -1)
    frame.Show()
    app.MainLoop()

但是当我关闭窗口时,它仍然在继续打印信息。

2 个回答

1

试着在 tickThread.start() 之前设置 tickThread.setDaemon(True)。这样做是因为守护线程应该和它的父线程一起结束。

7

这里没有什么神奇的 Frame.OnExit 方法。你把框架和应用搞混了。框架就像其他窗口一样,是可以关闭的,而应用是退出的。

所以,你可以把你的代码放在应用类的 OnExit 方法里。但这不是你想要的。

看看 这个简单的教程,了解一下 OnExit 方法。虽然这不是你想要的,但你应该知道它是怎么工作的(以及它是在哪个对象上被调用的)。

你可以把 EVT_CLOSE 绑定到你想在窗口中调用的任何东西。但你必须明确地这样做。

通常,你会把这个方法叫做 OnCloseOnCloseWindow。如果叫它 OnExit,只会让人更加困惑(就像现在这样)。

你绑定的事件处理方法必须是真正的事件处理方法,这意味着它需要接受一个 event 参数(还有 self)。

接下来,如果你添加了一个 EVT_CLOSE 处理器,你就覆盖了默认的处理器,这意味着除非你自己调用,否则 Destroy 不会被调用。

这里有一个 关于绑定 EVT_CLOSE 的教程,展示了上述所有步骤。

最后,正如 DavidRobinson 解释的,单纯做 tickThread.stopped 不会有任何效果;你必须把它设置为 True

把这些内容整合在一起:

class tradeWindow(wx.Frame):
    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, "Exchange", size = (500, 190))
        panel = wx.Panel(self)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
    def OnClose(self, event):
        tickThread.stopped = True
        self.Destroy()

还有一点:

在任何严肃的多线程程序中,如果你想在多个线程之间共享一个值,通常需要用某种同步对象来同步它。如果你在等待另一个线程的信号,通常的处理方式是使用 Condition。另一方面,如果你只是想共享一个值,可以使用 Lock

如果你真的知道自己在做什么,通常可以让全局解释器锁来为你处理同步。但一般来说,这不是个好主意。例如,你的主线程可能在核心 0 上运行,而你的后台线程在核心 1 上,Python 语言定义中没有任何东西能保证计算机会把核心 0 的缓存中的更新值复制到核心 1 上。所以,你的后台线程可能会一直在转圈,看到的是旧值,而永远得不到新值。实际上,在 x86 上的 CPython 2.0-3.3 中,这种情况不会发生,但除非你能证明这一点(或者至少识别出安全的情况),否则不要指望它。


最后,你问到守护线程是否是合适的解决方案。根据 文档

这个标志的意义在于,当只剩下守护线程时,整个 Python 程序会退出。

换句话说,你的程序可以在不停止守护线程的情况下退出。但是……

注意,守护线程在关闭时会被突然停止。它们的资源(比如打开的文件、数据库事务等)可能不会被正确释放。如果你希望线程优雅地停止,应该让它们不是守护线程,并使用合适的信号机制,比如 Event

撰写回答