Python 3.12线程创建报错RuntimeError:解释器关闭时无法创建新线程
鼠标晃动程序(Mouse Jiggler)线程创建报错问题
问题描述
我正在制作一款Mouse Jiggler(鼠标晃动程序),之前启动脚本循环后无法访问托盘图标,尝试用线程解决后,托盘图标及菜单能正常显示,但点击Start菜单时触发报错。
报错信息
start Running: False Running: True An error occurred when calling message handler Traceback (most recent call last): File "C:\Users\derby\AppData\Local\Programs\Python\Python312\Lib\site-packages\pystray\_win32.py", line 412, in _dispatcher return int(icon._message_handlers.get( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\derby\AppData\Local\Programs\Python\Python312\Lib\site-packages\pystray\_win32.py", line 224, in _on_notify descriptors[index - 1](self) File "C:\Users\derby\AppData\Local\Programs\Python\Python312\Lib\site-packages\pystray\_base.py", line 328, in inner callback(self) File "C:\Users\derby\AppData\Local\Programs\Python\Python312\Lib\site-packages\pystray\_base.py", line 453, in __call__ return self._action(icon, self) ^^^^^^^^^^^^^^^^^^^^^^^^ File "c:\Users\derby\PythonWorkspace\Mouse Jiggler\Jiggler copy.py", line 34, in start cl.change_running_state() File "c:\Users\derby\PythonWorkspace\Mouse Jiggler\Jiggler copy.py", line 28, in change_running_state t.start() File "C:\Users\derby\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 971, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't create new thread at interpreter shutdown
我的代码
from PIL import Image import pyautogui import time import pystray import threading import os class MyClass: def __init__(self): self.__running = False self.__stop_event = threading.Event() def run(self): while not self.__stop_event.is_set(): pyautogui.moveRel(50, 0, duration = 0) pyautogui.moveRel(-50,0, duration = 0) time.sleep(5) print("running") def change_running_state(self): print(f"Running: {self.__running}") self.__running = not self.__running print(f"Running: {self.__running}") if self.__running: self.__stop_event.clear() t = threading.Thread(target=self.run) t.start() else: self.__stop_event.set() if __name__ == "__main__": def start(icon, item): print("start") cl.change_running_state() def stop(icon, item): print("stop") cl.change_running_state() def exit_program(icon, item): print("exit") cl.change_running_state() icon.stop() os._exit(1) image = Image.open("macos.jpg") cl = MyClass() icon = pystray.Icon("macos", image) icon.menu=pystray.Menu( pystray.MenuItem("Start", start), pystray.MenuItem("Stop", stop), pystray.MenuItem("Exit", exit_program), ) icon.run_detached()
报错原因
这个报错不是Python 3.12的bug,核心原因是程序主线程已经退出,Python解释器进入shutdown状态,此时无法创建新线程。
你调用icon.run_detached()后,托盘图标在后台启动,主线程执行完所有代码后直接退出,解释器开始清理资源。这时候点击Start菜单触发创建线程的操作,就会触发RuntimeError。
解决方法
方法一:替换run_detached()为run()
icon.run()会阻塞主线程,让主线程一直保持运行状态,直到托盘图标停止。这是最直接的解决方式,符合pystray的常规使用逻辑:
# 替换原来的icon.run_detached() icon.run()
方法二:让主线程保持存活(适合必须用run_detached()的场景)
如果需要后台运行托盘图标,可以在主线程末尾添加等待逻辑,阻止主线程退出:
import signal # 注册信号处理,支持Ctrl+C优雅退出 def handle_exit(sig, frame): exit_program(icon, None) signal.signal(signal.SIGINT, handle_exit) # 让主线程无限等待 threading.Event().wait()
额外优化点
- 避免重复创建线程:多次点击Start会创建多个线程,需要在
MyClass中保存线程对象,启动前检查状态:
class MyClass: def __init__(self): self.__running = False self.__stop_event = threading.Event() self.__thread = None # 新增线程对象属性 def change_running_state(self): print(f"Running: {self.__running}") self.__running = not self.__running print(f"Running: {self.__running}") if self.__running: if self.__thread is None: # 仅当无活跃线程时启动 self.__stop_event.clear() self.__thread = threading.Thread(target=self.run) self.__thread.start() else: self.__stop_event.set() if self.__thread is not None: self.__thread.join() # 等待线程结束 self.__thread = None
- 优雅退出程序:
os._exit(1)过于粗暴,建议移除,icon.stop()后主线程会自然退出:
def exit_program(icon, item): print("exit") cl.change_running_state() icon.stop() # 无需os._exit(1)
修改后的完整代码
from PIL import Image import pyautogui import time import pystray import threading import os class MyClass: def __init__(self): self.__running = False self.__stop_event = threading.Event() self.__thread = None # 保存线程对象 def run(self): while not self.__stop_event.is_set(): pyautogui.moveRel(50, 0, duration=0) pyautogui.moveRel(-50, 0, duration=0) time.sleep(5) print("running") self.__thread = None # 线程结束后重置 def change_running_state(self): print(f"Running: {self.__running}") self.__running = not self.__running print(f"Running: {self.__running}") if self.__running: if self.__thread is None: self.__stop_event.clear() self.__thread = threading.Thread(target=self.run) self.__thread.start() else: self.__stop_event.set() if self.__thread is not None: self.__thread.join() if __name__ == "__main__": def start(icon, item): print("start") cl.change_running_state() def stop(icon, item): print("stop") cl.change_running_state() def exit_program(icon, item): print("exit") cl.change_running_state() icon.stop() image = Image.open("macos.jpg") cl = MyClass() icon = pystray.Icon("macos", image) icon.menu = pystray.Menu( pystray.MenuItem("Start", start), pystray.MenuItem("Stop", stop), pystray.MenuItem("Exit", exit_program), ) icon.run() # 使用run()替代run_detached()
内容的提问来源于stack exchange,提问作者Impaler67
相关产品推荐
相关产品推荐

