PyAutoGUI多线程运行异常求助:首次正常,再次运行报Tcl错误
Hey there, I’ve run into this exact issue before—let’s break down what’s happening and fix it.
First, let’s recap your problem: your script works perfectly the first time you run it, but on subsequent runs, you see start and end print immediately, then get hit with the Tcl_AsyncDelete: async handler deleted by the wrong thread error after a few seconds.
What’s causing this?
PyAutoGUI’s alert() function relies on Tkinter under the hood, and Tkinter is not thread-safe. When you run the script the first time, your worker thread creates a Tkinter instance to show the alerts. But when you re-run the script in the same interpreter session (like in an IDE or Jupyter notebook), leftover Tkinter resources from the first run clash with the new thread. The error happens because the wrong thread tries to clean up these leftover async handlers.
Let’s fix this with a few different approaches
Solution 1: Manually manage Tkinter resources in the thread
We’ll explicitly create and destroy a Tkinter root window in your worker thread to ensure no leftover state lingers between runs:
import time as t import pyautogui as p import threading import tkinter as tk def skincare1(): # Create a hidden Tkinter root window (required for PyAutoGUI alerts) root = tk.Tk() root.withdraw() p.alert("skincare level 1",'skincare 1') t.sleep(0.3) p.alert("skincare level 2",'skincare 2') # Destroy the root window to clean up all Tkinter resources root.destroy() print('start') threadem = threading.Thread(target=skincare1) threadem.start() print('end')
This ensures every thread run gets a fresh Tkinter environment, and we clean up fully when done.
Solution 2: Run all GUI operations in the main thread
Since Tkinter requires GUI actions to happen in the thread that created the root window (usually the main thread), we can use a queue to send alert requests from the worker thread to the main thread:
import time as t import pyautogui as p import threading import queue def skincare1(gui_queue): # Send alert requests to the main thread gui_queue.put(("show_alert", "skincare level 1", "skincare 1")) t.sleep(0.3) gui_queue.put(("show_alert", "skincare level 2", "skincare 2")) gui_queue.put(("done",)) def handle_gui_actions(gui_queue): # Create a single Tkinter root in the main thread import tkinter as tk root = tk.Tk() root.withdraw() while True: message = gui_queue.get() if message[0] == "show_alert": p.alert(message[1], message[2]) elif message[0] == "done": root.destroy() break print('start') queue = queue.Queue() # Start the worker thread worker_thread = threading.Thread(target=skincare1, args=(queue,)) worker_thread.start() # Handle GUI actions in the main thread handle_gui_actions(queue) print('end')
This follows Tkinter’s thread-safety rules strictly—all GUI work stays in the main thread, so no cross-thread resource conflicts.
Solution 3: Use multiprocessing instead of threading
If you want to avoid thread-related Tkinter headaches entirely, switch to multiprocessing. Each process has its own Python interpreter and Tkinter state, so there’s no cross-process resource clash:
import time as t import pyautogui as p import multiprocessing def skincare1(): p.alert("skincare level 1",'skincare 1') t.sleep(0.3) p.alert("skincare level 2",'skincare 2') print('start') process = multiprocessing.Process(target=skincare1) process.start() print('end')
This is the most straightforward fix if you don’t need shared memory between your main code and the alert logic.
内容的提问来源于stack exchange,提问作者Chris

