无法解决多线程中的Tcl_Asyncdelete

2024-10-17 08:19:17 发布

您现在位置:Python中文网/ 问答频道 /正文

我对Python(3.x)编程还很陌生,所以我最近决定着手处理我的第一个实际项目,并试图制作一个非常简单的客户机/服务器聊天程序。但是,在我终止客户端或服务器端的tkinter窗口之后,总是会出现一个错误:Tcl_AsyncDelete:async handler被错误的线程删除。在

我已经搜索并找到了关于这个的线索,但是我仍然不明白哪里错了,因为我觉得我找到的其他答案并不真正适合我的应用程序环境。据我所知,这个错误是由于在创建的线程之外终止一个tkinter窗口引起的,但是我看不出这是怎么发生的。请帮忙,提前谢谢!在

服务器代码:

import socket
from tkinter import *
from tkinter.messagebox import *
from threading import *
import tkinter.scrolledtext as tkst
import sys


def main():

    global c
    q = socket.socket()
    q.bind(("", 0))
    open_port = (q.getsockname()[1])
    q.close()

    menu = Tk()
    menu.configure(bg="black")
    menu.iconbitmap("chat.ico")
    menu.title("chat")

    Label(fg="white", bg="black", text="Set host IP:", font=("Comic Sans", 14)).grid(row=0, column=0, padx=10, pady=10)
    Label(fg="white", bg="black", text="Set host port:", font=("Comic Sans", 14)).grid(row=1, column=0, padx=10, pady=10)
    Label(fg="white", bg="black", text="Username:", font=("Comic Sans", 14)).grid(row=2, column=0, padx=10, pady=10)

    ip_enter = Entry(width=16, font=("Comic Sans", 14))
    ip_enter.insert(END, socket.gethostbyname(socket.gethostname()))
    port_enter = Entry(width=16, font=("Comic Sans", 14))
    port_enter.insert(END, open_port)
    user_enter = Entry(width=16, font=("Comic Sans", 14))
    ip_enter.grid(row=0, column=1, padx=10, pady=10)
    port_enter.grid(row=1, column=1, padx=10, pady=10)
    user_enter.grid(row=2, column=1, padx=10, pady=10)

    def startup():
        try:
            host = ip_enter.get()
            port = port_enter.get()
            username = user_enter.get()
            if username == "":
                raise ValueError("username cannot be empty")

            s = socket.socket()
            s.bind((host, int(port)))

            menu.destroy()

            chat = Tk()
            chat.configure(bg="black")
            chat.iconbitmap("chat.ico")
            chat.title("chat")

            def closeout():
                s.close()
                chat.destroy()

            chat.protocol("WM_DELETE_WINDOW", closeout)

            chatbox = tkst.ScrolledText(state=DISABLED)
            chatbox.grid(row=0, column=0, columnspan=2, padx=2, pady=10)
            message = Entry(width=80, font=("Comic Sans", 10))
            message.grid(row=1, column=0, padx=10, pady=10)

            def sync():
                s.listen(1)
                global c
                c, addr = s.accept()
                c.send(("Connected to host " + username + "\n").encode("UTF-8"))
                data = (c.recv(1024)).decode("UTF-8")
                chatbox.config(state=NORMAL)
                chatbox.insert(END, (data))
                chatbox.config(state=DISABLED)
                chatbox.see("end")

                def rec():
                    while True:
                        try:
                            data = (c.recv(1024)).decode("UTF-8")
                            if data:
                                chatbox.config(state=NORMAL)
                                chatbox.insert(END, (data))
                                chatbox.config(state=DISABLED)
                                chatbox.see("end")
                            else:
                                break
                        except:
                            break

                rec = Thread(target=rec)
                rec.start()

            t2 = Thread(target=sync)
            t2.start()

            def senddata(event):
                try:
                    chatbox.config(state=NORMAL)
                    if message.get() != "":
                        mesg = ("<" + username + "> " + message.get() + "\n")
                        message.delete(0, "end")
                        chatbox.insert(END, mesg)
                        chatbox.config(state=DISABLED)
                        c.send(mesg.encode("UTF-8"))
                        chatbox.see("end")
                except OSError:
                    useless = 1
            send = Button(bg="white", text="Send", font=("Comic Sans", 14))
            send.bind("<Button-1>", senddata)
            send.grid(row=1, column=1, padx=10, pady=10)
            chat.bind("<Return>", senddata)

            chat.mainloop()

        except OSError:
            showerror("Error", "Unable to bind to given IP or Port")
        except ValueError:
            showerror("Error", "Username field cannot be empty")

    connect = Button(bg="white", text="Start", font=("Comic Sans", 14), command=startup)
    connect.grid(row=3, column=1, padx=10, pady=10)

    menu.mainloop()

if __name__ == '__main__':
    main()

客户代码:

^{pr2}$

Tags: portchatcolumnsocketgridrowbgstate
1条回答
网友
1楼 · 发布于 2024-10-17 08:19:17

我在代码中通过触发垃圾回收解决了这个问题。在

这是一种可能发生此问题的方法,即使您不从线程调用tkinter代码。如果您删除了一个tkinter窗口或小部件,那么Python最终将为这些小部件调用delete方法。当Python的垃圾回收决定在线程运行时释放这些对象时,就会出现问题。换句话说,垃圾回收在线程的上下文中运行,而不是在主线程中运行。在

解决方法:

当您删除tkinter小部件(如按钮)时,会触发垃圾回收。”Delete“可以简单地说,您重复使用了一个用来保存tkinter小部件的变量名。在

在代码中找到释放tkinter资源的位置(删除小部件),并调用Python的垃圾回收来触发一个立即运行的收集。在

import gc
.....
connect = tk.Button(.....)
....
#later when you're done with the button
connect = None     # make sure the reference count to the tk.Button is zero
gc.collect()       # force Python's garbage collect to run

这个完全修复了我在多线程环境中运行tkinter时发生的随机aync delee崩溃。在

相关问题 更多 >