You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()

额外优化点

  1. 避免重复创建线程:多次点击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
  1. 优雅退出程序: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 18:24:59