如何在通过任务计划程序终止代码时仍执行finally块?
问题原因
Windows任务计划终止任务时,默认会发送强制终止指令(类似Unix的SIGKILL),直接终止进程,不会给Python程序执行finally块的机会。而PyCharm/控制台终止时,发送的是正常中断信号(如SIGINT),Python会触发异常处理流程,执行finally代码。
解决方案
1. 捕获Windows控制台关闭事件
通过pywin32库注册控制台关闭事件处理器,在任务计划终止程序时触发自定义清理逻辑。
首先安装依赖:
pip install pywin32
修改代码如下:
import time from datetime import datetime import win32api import win32con def log_info(message: str): with open('somefile.log', 'a') as file: file.write(f'{datetime.now()}: {message}\n') def handle_termination(event): # 处理控制台关闭、任务终止、系统注销/关机等事件 if event in (win32con.CTRL_C_EVENT, win32con.CTRL_CLOSE_EVENT, win32con.CTRL_LOGOFF_EVENT, win32con.CTRL_SHUTDOWN_EVENT): log_info('Process ended (terminated by task scheduler)') return True # 告知系统已处理事件 # 注册事件处理器 win32api.SetConsoleCtrlHandler(handle_termination, True) try: log_info('Process started') time.sleep(1000) # 模拟长时间运行 finally: # 正常退出时仍会执行 log_info('Process ended (normal exit)')
2. 封装为Windows服务
将脚本包装成Windows服务,服务的终止会触发标准的stop事件,确保清理逻辑稳定执行。
示例代码(依赖pywin32):
import win32serviceutil import win32service import win32event import time from datetime import datetime def log_info(message: str): with open('somefile.log', 'a') as file: file.write(f'{datetime.now()}: {message}\n') class MyService(win32serviceutil.ServiceFramework): _svc_name_ = "LongRunningPythonService" _svc_display_name_ = "Long Running Python Service" def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) self.running = True def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) self.running = False log_info('Process ended (service stopped)') def SvcDoRun(self): log_info('Process started (service running)') while self.running: time.sleep(10) # 模拟业务逻辑 if __name__ == '__main__': win32serviceutil.HandleCommandLine(MyService)
安装服务(需管理员权限控制台):
python your_script.py install
启动服务:
python your_script.py start
任务计划可配置为启动/停止该服务,终止时会自动触发SvcStop方法执行清理。
3. 调整任务计划终止策略
在任务计划的设置选项卡中,找到“当请求停止任务时”,选择允许任务在请求停止时完成,而非“立即终止任务”。此设置需配合方案1使用,确保程序能响应终止请求并执行清理。
内容的提问来源于stack exchange,提问作者Marty
相关产品推荐
相关产品推荐

