如何通过按钮停止Python中运行服务器函数的线程?
问题修复方案
核心问题分析
你的两种方案均失效,根源在于两个关键问题:
server.accept()是阻塞调用:线程会一直卡在这个方法上,直到有客户端连接,根本没机会检测终止flag,导致设置flag后线程无法退出循环。- GUI线程调用
thread.join()阻塞UI:join()是阻塞方法,在GUI主线程执行会直接冻结窗口事件循环——而线程卡在accept上无法结束,最终导致窗口无限冻结。
修复方案一:给Socket设置超时,定期检测终止信号
修改server.py的startChat函数,给服务器socket设置超时时间,让accept()定期抛出超时异常,借此机会检测终止flag:
# server.py 修改后的startChat函数 def startChat(): print("server is working on " + SERVER) server.listen(30) server.settimeout(1) # 设置1秒超时,可根据需求调整 try: while not flag.is_set(): try: conn, addr = server.accept() name = conn.recv(1024).decode(FORMAT) clients.append(conn) # 后续客户端逻辑可在此扩展 except socket.timeout: # 超时后回到循环开头,检测终止flag continue except Exception as e: print(traceback.format_exc()) finally: # 清理socket,避免资源泄漏 server.close()
同时修改mainmenu.py的leavewindow方法,避免在GUI线程直接调用join,改用新线程处理等待逻辑:
# mainmenu.py 修改后的leavewindow方法 def leavewindow(self): serv.terminate() # 启动新线程等待服务器线程结束,不阻塞GUI def wait_for_thread(): self.thread.join() print("Thread closed") serv.commence() # 回到GUI线程销毁窗口 self.master2.after(0, self.master2.destroy) Thread(target=wait_for_thread).start()
修复方案二:关闭Server Socket强制中断Accept
当需要停止线程时,直接关闭服务器socket,accept()会立即抛出异常,线程可快速退出:
修改server.py
新增关闭socket的逻辑,并支持重新创建socket:
# server.py flag = threading.Event() # ...其他原有代码... def terminate(): flag.set() # 关闭server socket,强制中断accept调用 global server try: server.close() except: pass def commence(): flag.clear() # 重新创建socket,确保可再次启动服务器 global server server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(ADDRESS) def startChat(): print("server is working on " + SERVER) server.listen(30) try: while not flag.is_set(): conn, addr = server.accept() name = conn.recv(1024).decode(FORMAT) clients.append(conn) except Exception as e: # 关闭socket时的异常属于正常终止,可选择性忽略 if not flag.is_set(): print(traceback.format_exc())
修改mainmenu.py的leavewindow方法
同样用新线程处理join,避免阻塞GUI:
def leavewindow(self): serv.terminate() def wait_for_thread(): self.thread.join() print("Thread closed") serv.commence() self.master2.after(0, self.master2.destroy) Thread(target=wait_for_thread).start()
额外注意事项
- 终止线程前,建议关闭所有已连接的客户端socket,避免资源泄漏。
- 方案二中的
commence()函数重新创建了socket,确保服务器可以被多次启动/停止。
内容的提问来源于stack exchange,提问作者N. Tanaka
相关产品推荐
相关产品推荐

