Socket聊天App开发遇AttributeError:'App'对象无'gui_done'属性
Socket聊天应用线程同步问题
我在开发Socket聊天应用时,遇到GUI线程与Socket监听线程的同步问题,导致运行时触发属性错误。
代码实现
import socket import threading from tkinter import Tk, Label, ScrolledText, Entry, Button, END class App(): def __init__(self, user): self.user=user self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.clientSocket.connect(("127.0.0.1", 9090)) guit=threading.Thread(target=self.gui) lstn=threading.Thread(target=self.listen) guit.start() lstn.start() def gui(self): self.root=Tk() self.username=Label(self.root, font=("Arial", 15), text=self.user) self.username.grid(column=0, row=0) self.hist=ScrolledText(self.root, width=40, height=25 , font=("Arial",15)) self.hist.config(state=DISABLED) self.hist.grid(column=0,row=1, columnspan=2) self.msg=Entry(self.root, font=("Arial",15)) self.msg.grid(row=2,column=0) btnSend=Button(self.root, font=("Arial",15), text="Send", command=self.send) btnSend.grid(row=2, column=1) self.gui_done=True self.root.bind("<Return>", self.send) self.root.mainloop() def addMessage(self, message): self.hist.configure(state=NORMAL) self.hist.insert(END, f'{message}\n') self.hist.configure(state=DISABLED) def send(self,e=None): self.clientSocket.send(f'[{self.user}]{self.msg.get()}'.encode()) print(self.msg.get()) self.msg.delete(0,END) def listen(self): self.clientSocket.send(f'[SERVER]{self.user} has joined'.encode()) while 1: msgFromServer=self.clientSocket.recv(1024) print(msgFromServer) if self.gui_done: self.addMessage(msgFromServer.decode())
报错信息
Exception in thread Thread-2 (listen): Traceback (most recent call last): File "C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.2288.0_x64__qbz5n2kfra8p0\\lib\\threading.py", line 1016, in _bootstrap_inner self.run() File "C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.2288.0_x64__qbz5n2kfra8p0\\lib\\threading.py", line 953, in run self._target(*self._args, **self._kwargs) File "C:\\code\\https\\qttest\\client1.py", line 52, in listen if self.gui_done: AttributeError: 'App' object has no attribute 'gui_done'
问题根源
listen线程与gui线程几乎同时启动,listen线程可能在gui线程完成初始化并设置self.gui_done属性前,就尝试访问该属性,触发AttributeError。
解决方案
方案1:提前初始化属性
在类的构造函数中提前定义self.gui_done并设为False,避免属性未定义的错误:
def __init__(self, user): self.user=user self.gui_done = False # 提前初始化属性 self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.clientSocket.connect(("127.0.0.1", 9090)) guit=threading.Thread(target=self.gui) lstn=threading.Thread(target=self.listen) guit.start() lstn.start()
方案2:使用线程Event实现可靠等待
用threading.Event替代布尔变量,让listen线程等待GUI初始化完成后再处理消息,更安全可靠:
import threading class App(): def __init__(self, user): self.user=user self.gui_ready = threading.Event() # 创建同步事件 self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.clientSocket.connect(("127.0.0.1", 9090)) guit=threading.Thread(target=self.gui) lstn=threading.Thread(target=self.listen) guit.start() lstn.start() def gui(self): self.root=Tk() # 保留原GUI初始化代码 self.username=Label(self.root, font=("Arial", 15), text=self.user) self.username.grid(column=0, row=0) self.hist=ScrolledText(self.root, width=40, height=25 , font=("Arial",15)) self.hist.config(state=DISABLED) self.hist.grid(column=0,row=1, columnspan=2) self.msg=Entry(self.root, font=("Arial",15)) self.msg.grid(row=2,column=0) btnSend=Button(self.root, font=("Arial",15), text="Send", command=self.send) btnSend.grid(row=2, column=1) self.gui_ready.set() # 标记GUI初始化完成 self.root.bind("<Return>", self.send) self.root.mainloop() def listen(self): self.clientSocket.send(f'[SERVER]{self.user} has joined'.encode()) self.gui_ready.wait() # 等待GUI准备完成 while 1: msgFromServer=self.clientSocket.recv(1024) print(msgFromServer) self.addMessage(msgFromServer.decode())
方案3:确保GUI操作在主线程执行
Tkinter要求所有GUI操作必须在创建它的线程中执行,若后续出现GUI卡顿或报错,可通过self.root.after()将addMessage调用切换到GUI线程:
# 在listen线程中修改调用方式 self.root.after(0, self.addMessage, msgFromServer.decode())
内容的提问来源于stack exchange,提问作者digital_coder
相关产品推荐
相关产品推荐

