Python QApplication启动子线程后无法正常关闭问题
问题解决:Qt应用关闭时因后台线程阻塞无法终止
问题根源
后台线程中调用的sched.scheduler.run()是阻塞式方法,会一直等待调度的备份任务执行完毕才结束线程。而Python默认线程为非守护线程,主线程(Qt应用)退出时会等待所有非守护线程完成,导致应用冻结无法正常终止。
解决方案1:将线程设为守护线程
直接把后台线程标记为守护线程,主线程退出时会自动终止该线程,实现快速关闭:
if __name__ == '__main__': # 设置daemon=True将线程转为守护线程 x = threading.Thread(target=backupGiornaliero, daemon=True) x.start() app = QApplication(sys.argv) vistaLogin = LoginView(app) vistaLogin.show() sys.exit(app.exec())
解决方案2:优雅终止调度器(推荐)
如果需要避免强制中断备份任务,可监听Qt应用的aboutToQuit信号,在关闭时主动终止调度器的阻塞状态:
首先修改backupGiornaliero函数,添加终止标记并循环检查调度状态:
import threading # 线程终止标记 stop_scheduler = threading.Event() def backupGiornaliero(): global stop_scheduler dataOggi=datetime.today().strftime('%Y-%m-%d') orarioBackup = " 22:00:00" dataOrarioBackup = str(dataOggi+orarioBackup) datatimeBackup = datetime.strptime(dataOrarioBackup, '%Y-%m-%d %H:%M:%S') if datetime.now() < datatimeBackup: unixDataBackup = time.mktime(datatimeBackup.timetuple()) else: unixDataBackup = time.mktime(datatimeBackup.timetuple())+ float(86400) print("unix timestamp dell'orario di backup di oggi "+str(unixDataBackup)) s = sched.scheduler(time.time, time.sleep) s.enterabs(unixDataBackup, 0, BackupModel().eseguiBackup) # 循环执行调度,同时检查终止信号 while not stop_scheduler.is_set(): # 非阻塞执行调度事件,超时1秒避免永久阻塞 remaining = s.run(blocking=False) if remaining is None: break time.sleep(1) print("备份线程已正常终止")
然后在主函数中绑定应用退出信号:
if __name__ == '__main__': x = threading.Thread(target=backupGiornaliero) x.start() app = QApplication(sys.argv) # 应用退出时触发线程终止 def on_app_quit(): global stop_scheduler stop_scheduler.set() x.join() # 等待线程正常退出 app.aboutToQuit.connect(on_app_quit) vistaLogin = LoginView(app) vistaLogin.show() sys.exit(app.exec())
方案对比
- 方案1实现简单,但可能中断正在执行的备份任务;
- 方案2更优雅,确保线程正常退出,避免备份任务被强制中断。
内容的提问来源于stack exchange,提问作者Emanuele
相关产品推荐
相关产品推荐

