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

能否让线程终止Python主脚本?邮件转发程序紧急停止需求

问题:如何让监控ESC键的线程终止主Python脚本?

我编写了一个自动转发邮件的Python程序,希望添加ESC键作为紧急停止按键,触发后主脚本抛出异常并终止程序。但尝试使用raise Exception和sys.exit(1)时,仅会终止监控线程,主脚本仍会继续运行。

原代码如下:

import pyautogui as key
import keyboard as kb
import time
import threading

# Thread
def CheckPress():
    while 1:
        if kb.is_pressed("esc"):
            raise Exception  # End Program of "esc" is pressed
            break
        else:
            time.sleep(0.1)

checkThread = threading.Thread(target=CheckPress)
checkThread.start()

# My Main Code
NumberOfEmails = 100  # How many emails to send
for count in range(NumberOfEmails):
    # gmail forward hotkey
    key.press("f")
    time.sleep(1)

    # type email
    key.write("email@gmail.com", interval=0.05)
    time.sleep(1)

    # send email
    key.hotkey("ctrl", "Enter")
    time.sleep(1)

    # move to next email
    key.hotkey("j")
    time.sleep(1)

请问是否可以让线程终止主脚本?如果可以,该如何实现?


解决方案

可以实现,核心是让按键监听逻辑直接终止整个Python进程,而非仅终止线程。以下是两种可行方案:

方案1:改进线程监听,使用os._exit()终止进程

os._exit()会直接在操作系统层面终止整个进程,所有线程都会被停止,能直接终止主脚本。同时将线程设为守护线程,避免主进程意外退出后线程残留。

修改后的代码:

import pyautogui as key
import keyboard as kb
import time
import threading
import os

def CheckPress():
    while True:
        if kb.is_pressed("esc"):
            print("ESC键被按下,正在终止程序...")
            os._exit(1)  # 直接终止整个进程
        time.sleep(0.1)

# 设置为守护线程,主进程退出时自动终止该线程
checkThread = threading.Thread(target=CheckPress, daemon=True)
checkThread.start()

# 主代码部分
NumberOfEmails = 100
for count in range(NumberOfEmails):
    print(f"正在处理第 {count+1} 封邮件")
    key.press("f")
    time.sleep(1)
    key.write("email@gmail.com", interval=0.05)
    time.sleep(1)
    key.hotkey("ctrl", "Enter")
    time.sleep(1)
    key.hotkey("j")
    time.sleep(1)

方案2:使用keyboard.add_hotkey简化监听(推荐)

keyboard库自带add_hotkey方法,会在后台自动管理监听线程,无需手动创建线程,代码更简洁。

修改后的代码:

import pyautogui as key
import keyboard as kb
import time
import os

# 注册ESC热键,触发时直接终止进程
kb.add_hotkey("esc", lambda: os._exit(1))

# 主代码部分
NumberOfEmails = 100
for count in range(NumberOfEmails):
    print(f"正在处理第 {count+1} 封邮件")
    key.press("f")
    time.sleep(1)
    key.write("email@gmail.com", interval=0.05)
    time.sleep(1)
    key.hotkey("ctrl", "Enter")
    time.sleep(1)
    key.hotkey("j")
    time.sleep(1)

补充说明

  • 之前使用raise Exception和sys.exit(1)无效的原因:sys.exit()本质是抛出SystemExit异常,线程内的异常仅会终止当前线程,不会传递到主线程;raise Exception同理,仅能终止监听线程,无法影响主脚本执行。
  • os._exit()与sys.exit()的区别:sys.exit()是请求退出当前线程,而os._exit()是直接终止整个进程,所有线程都会被强制停止。

内容的提问来源于stack exchange,提问作者Carter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:57:04