Search code examples
pythonmultithreadingpython-2.7wxpython

What are the bad effects of not stopping/killing all the spawned threads in python/wxPython


I am working on a GUI app with python v2.7 and wxPython 3.0 on windows 7 OS. I have a general question regarding threads. In my programm I am creating a thread that will update my GUI continuously. If I close the GUI window it seems that everything is closed and is fine. How ever upon executing my code from command prompt I observed that the thread keeps sending updates to the GUI.

Question: Since my GUI is updating properly and even closes without any crashes should I bother about the thread that is still working hard to update my GUI after I have closed the GUI window? If this is a very serious concern?

It would be great if someone can suggest that how can I can modify my closeWindow() so that when the GUI is closed, it first kills the thread.

Code: Here is a sample code. Please execute the code from console/cmd then you'll notice my problem.

import wx
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub
import time
from threading import Thread

class GUI(wx.Frame):
    def __init__(self, parent, id, title):
        screenWidth = 500
        screenHeight = 400
        screenSize = (screenWidth,screenHeight)
        wx.Frame.__init__(self, None, id, title, size=screenSize)
        self.locationFont = locationFont = wx.Font(12, wx.MODERN, wx.NORMAL, wx.BOLD)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        myPanelA = wx.Panel(self, style=wx.SIMPLE_BORDER)
        myPanelA.SetBackgroundColour('#C0FAE0')
        self.myTextA = wx.StaticText(myPanelA, -1, "Testing")
        mainSizer.Add(myPanelA, 1, wx.EXPAND, 5)
        self.SetSizer(mainSizer)
        self.Bind(wx.EVT_CLOSE, self.closeWindow)
        pub.subscribe(self.updatePanelA, 'Update-panelA')

    def updatePanelA(self, message):
        self.myTextA.SetLabel(message)

    def closeWindow(self, event):
        # -->  Code for killing the thread :)
         self.Destroy()

class threadA(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.start()
    def run(self):
        ObjA = updateGUI()
        ObjA.methodA()

class updateGUI():
    def methodA(self):
        while True:
            time.sleep(2)
            print 'Sending update'
            wx.CallAfter(pub.sendMessage, 'Update-panelA', message='Yes, It works')

if __name__=='__main__':
    app = wx.App()
    frame = GUI(parent=None, id=-1, title="Problem Demo-PSS")
    frame.Show()
    threadA()
    app.MainLoop()

Thank you for your time and any help will be appreciated.


Solution

  • Alright, the way it was is not ok, the thread was left alive continually sending updates to a GUI that didn't exist. This caused it to throw a nasty error about the C++ bit not being real or some such nonsense. But you can avoid this pretty easily by making the following changes:

    1. Create the thread within the GUI's __init__() method and create it as a class attribute
    2. Have the closeWindow() method of your GUI unsubscribe itself from the thread (to protect against transmission after closing
    3. Signal the thread to stop transmitting by altering a parameter I added (running)
    4. Create the thread as a daemon
    5. Modify the while loop in the thread to be based off the new running parameter so it can be told when to stop
    6. Tell app to not have redirect (app = wx.App(False))

    Take all of that into account and you're left with the following code:

    import wx
    from wx.lib.pubsub import setupkwargs
    from wx.lib.pubsub import pub
    import time
    import threading
    
    class GUI(wx.Frame):
        def __init__(self, parent, id, title):
            screenWidth = 500
            screenHeight = 400
            screenSize = (screenWidth,screenHeight)
            wx.Frame.__init__(self, None, id, title, size=screenSize)
            self.locationFont = locationFont = wx.Font(12, wx.MODERN, wx.NORMAL, wx.BOLD)
            mainSizer = wx.BoxSizer(wx.VERTICAL)
            myPanelA = wx.Panel(self, style=wx.SIMPLE_BORDER)
            myPanelA.SetBackgroundColour('#C0FAE0')
            self.myTextA = wx.StaticText(myPanelA, -1, "Testing")
            mainSizer.Add(myPanelA, 1, wx.EXPAND, 5)
            self.SetSizer(mainSizer)
            self.Bind(wx.EVT_CLOSE, self.closeWindow)
            pub.subscribe(self.updatePanelA, 'Update-panelA')
    
    
            self.thd = threadA()
    
        def updatePanelA(self, message):
            self.myTextA.SetLabel(message)
    
        def closeWindow(self, event):
            # -->  Code for killing the thread :)
            pub.unsubscribe(self.updatePanelA, 'Update-panelA')
            self.thd.running = False
            self.Destroy()
    
    class threadA(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.daemon = True
            self.running = True
            self.start()
    
        def run(self):
            while self.running:
                time.sleep(2)
                print 'Sending update'
                wx.CallAfter(pub.sendMessage, 'Update-panelA', message='Yes, It works')        
    
    if __name__=='__main__':
        app = wx.App(False)
        frame = GUI(parent=None, id=-1, title="Problem Demo-PSS")
        frame.Show()
        app.MainLoop()
    

    Hope this helps