【问题标题】:Tcl_AsyncDelete Error Multithreading PythonTcl_AsyncDelete 错误多线程 Python
【发布时间】:2015-01-20 07:56:34
【问题描述】:

听说Python中的线程不好处理,和tkinter比较纠结。

我有以下问题。我有两个类,一个用于 GUI,另一个用于无限进程(我必须对两者都使用类)。首先,我启动 GUI 类,然后启动无限进程类。我希望当你关闭 GUI 时,它也完成了无限过程并且程序结束。

代码的简化版本如下:

import time, threading
from tkinter import *
from tkinter import messagebox

class Interface(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib1 = "Attrib from Interface class"

    def run(self): 
        #Main Window
        self.mainWindow = Tk()
        self.mainWindow.geometry("200x200")
        self.mainWindow.title("My GUI Title")
        self.mainWindow.protocol("WM_DELETE_WINDOW", self.quit)
        #Label
        lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
        #Start
        self.mainWindow.mainloop()

    #The Interface class contains methods that use attributes from itself and attributes from Process class.
    def method1(self): 
        print(self.attrib1)
        print(SecondThread.attrib2)

    def quit(self):
        if messagebox.askyesno('App','Are you sure you want to quit?'):
            #In order to use quit function, mainWindow MUST BE an attribute of Interface. 
            self.mainWindow.destroy()
            self.mainWindow.quit()  

class Process(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib2 = "Attrib from Process class"

    def run(self):
        global finish
        while not finish:
            print("Proceso infinito")
            #Inside the infinite process a method from Interface class is used.
            GUI.method1()
            time.sleep(3)

finish = False  
#Starts the GUI
GUI = Interface()
GUI.start()
#Starts the infinity process
SecondThread = Process()
SecondThread.start()    
#Waits until GUI is closed
GUI.join()
print("When GUI is closed this message appears")
#When GUI is closed we set finish to True, so SecondThread will be closed.
finish = True
#After all the program should finish but it raises the error: Tcl_AsyncDelete: async handler deleted by the wrong thread

感谢您的帮助!

【问题讨论】:

  • "#After all the program should finish but it raise the error: Tcl_AsyncDelete: async handler deleted by the wrong thread" not show to me in python3.3 ubuntu.
  • 这个错误似乎只发生在 Windows.. 很奇怪..

标签: python multithreading tkinter tcl


【解决方案1】:

我使用垃圾回收做了一个粗略的修改。

import time, threading, gc
from tkinter import *
from tkinter import messagebox

class Interface(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib1 = "Attrib from Interface class"

    def run(self):
        #Main Window
        self.mainWindow = Tk()
        self.mainWindow.geometry("200x200")
        self.mainWindow.title("My GUI Title")
        self.mainWindow.protocol("WM_DELETE_WINDOW", self.quit)
        #Label
        lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
        #Start
        self.mainWindow.mainloop()

    #The Interface class contains methods that use attributes from itself and attributes from Process class.
    def method1(self):
        print(self.attrib1)
        print(SecondThread.attrib2)

    def quit(self):
        global finish
        if messagebox.askyesno('App','Are you sure you want to quit?'):
            #In order to use quit function, mainWindow MUST BE an attribute of Interface.
            self.mainWindow.destroy()
            del self.mainWindow
            gc.collect()
            finish = True

class Process(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib2 = "Attrib from Process class"

    def run(self):
        global finish
        while not finish:
            print("Proceso infinito")
            #Inside the infinite process a method from Interface class is used.
            GUI.method1()
            time.sleep(3)

finish = False
#Starts the GUI
GUI = Interface()
GUI.start()
#Starts the infinity process
SecondThread = Process()
SecondThread.start()
#Waits until GUI is closed
GUI.join()
#print("When GUI is closed this message appears")
#When GUI is closed we set finish to True, so SecondThread will be closed.
#finish = True

【讨论】:

    【解决方案2】:

    这是因为您在线程上创建了 Tk 主窗口,并且您没有在进程主线程上运行 UI。当您退出进程时,将从进程主线程进行清理。此处示例的最简单解决方案是在主线程(进程默认线程)上创建 UI,并且仅将另一个线程用于工作任务。如果您的实际应用程序无法在主线程上创建 UI,您将需要考虑从其自己的线程中终止 Tk。删除 Tcl 解释器可能会为您做到这一点。

    我修改了示例代码以表明将 UI 保留在主线程上可以避免出现此错误消息。由于您希望在创建 UI 之后但在它运行之前创建您的工作者,我们可以使用 Tk after 方法在 Tk 主循环运行时启动工作者。

    import time, threading
    from tkinter import *
    from tkinter import messagebox
    
    class Interface:
        def __init__(self):
            #threading.Thread.__init__(self)
            self.attrib1 = "Attrib from Interface class"
            #Main Window
            self.mainWindow = Tk()
            self.mainWindow.geometry("200x200")
            self.mainWindow.title("My GUI Title")
            self.mainWindow.protocol("WM_DELETE_WINDOW", self.quit)
            #Label
            lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
    
        #def run(self): 
    
        def start(self): #Start
            self.mainWindow.mainloop()
    
        #The Interface class contains methods that use attributes from itself and attributes from Process class.
        def method1(self): 
            print(self.attrib1)
            print(SecondThread.attrib2)
    
        def quit(self):
            if messagebox.askyesno('App','Are you sure you want to quit?'):
                #In order to use quit function, mainWindow MUST BE an attribute of Interface. 
                self.mainWindow.destroy()
                self.mainWindow.quit()  
    
    class Process(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.attrib2 = "Attrib from Process class"
    
        def run(self):
            global finish
            while not finish:
                print("Proceso infinito")
                #Inside the infinite process a method from Interface class is used.
                GUI.method1()
                time.sleep(3)
    
    finish = False
    #Starts the GUI
    
    GUI = Interface()
    #Starts the infinity process
    SecondThread = Process()
    GUI.mainWindow.after(50, SecondThread.start)   
    #Waits until GUI is closed
    GUI.start()
    #GUI.join()
    print("When GUI is closed this message appears")
    #When GUI is closed we set finish to True, so SecondThread will be closed.
    finish = True
    #After all the program should finish but it raises the error: Tcl_AsyncDelete: async handler deleted by the wrong thread
    

    【讨论】:

    • “如果您的实际应用程序无法在主线程上创建 UI,您将需要考虑从其自己的线程中终止 Tk。删除 Tcl 解释器可能会为您做到这一点。”有什么提示吗?我不能在主线程上运行 mainloop,必须在同一线程上创建 UI。我将如何在创建 UI 的线程中终止 UI?
    猜你喜欢
    • 1970-01-01
    • 2015-01-24
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    相关资源
    最近更新 更多