如何通过QButton终止concurrent.future中的线程任务
解决concurrent.futures线程无法终止的问题
原代码存在的问题
- 使用
with ThreadPoolExecutor会在Start_Button_clicked函数执行完毕后自动关闭线程池,导致后续无法通过线程池控制任务。 - 错误调用
Future对象的shutdown方法——shutdown是ThreadPoolExecutor的方法,而非Future对象的方法。 - 任务函数
task_work未检查停止信号,即使修改Stop_Work变量,任务仍会继续执行。 - 普通全局布尔变量作为停止信号存在线程安全隐患,且Qt主线程中调用
time.sleep(1)会导致UI卡顿。
解决方案
- 用
threading.Event作为线程安全的停止信号,替代全局布尔变量。 - 将
ThreadPoolExecutor设为类成员变量,避免with语句自动关闭线程池,保留控制权。 - 在
task_work循环中检查停止信号,触发后立即终止任务。 - 控制按钮状态防止重复提交任务,移除主线程中的阻塞操作。
修正后的完整代码
import time import threading from concurrent.futures import ThreadPoolExecutor from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton class MyApp(QMainWindow): def __init__(self): super().__init__() # 窗口设置 self.setWindowTitle("线程控制示例") self.setFixedSize(228, 150) # 线程安全的停止信号 self.stop_event = threading.Event() # 线程池实例(类成员) self.executor = None self.future = None # 按钮初始化 self.start_btn = QPushButton("Start", self) self.start_btn.setGeometry(25, 20, 178, 41) self.start_btn.clicked.connect(self.on_start_clicked) self.stop_btn = QPushButton("Stop", self) self.stop_btn.setGeometry(25, 89, 178, 41) self.stop_btn.clicked.connect(self.on_stop_clicked) self.stop_btn.setEnabled(False) def on_start_clicked(self): print("开始按钮被点击") # 重置停止信号(允许任务运行) self.stop_event.set() # 初始化线程池 self.executor = ThreadPoolExecutor(max_workers=2) # 提交任务 self.future = self.executor.submit(self.task_work) # 更新按钮状态 self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) def on_stop_clicked(self): print("停止按钮被点击") # 触发停止信号 self.stop_event.clear() # 取消未执行任务并关闭线程池 if self.executor: self.executor.shutdown(wait=False, cancel_futures=True) # 更新按钮状态 self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) def task_work(self): try: i = 0 for _ in range(10): i += 1 print(f"I: {i}") # 检查停止信号,触发则终止任务 if not self.stop_event.is_set(): print("任务被终止") break # 模拟任务耗时 time.sleep(2.5) else: print("任务正常完成") finally: # 确保线程池资源释放 if self.executor: self.executor.shutdown(wait=False) if __name__ == '__main__': app = QApplication([]) window = MyApp() window.show() app.exec()
关键修改说明
- 线程安全信号:
threading.Event()通过set()/clear()/is_set()实现线程间安全的状态交互,避免全局变量的线程冲突。 - 线程池控制权:将线程池设为类成员,保留对其的控制能力,可在停止时主动调用
shutdown取消任务。 - 任务内终止逻辑:在
task_work的循环中实时检查停止信号,确保触发停止后任务能立即退出。 - UI状态优化:通过按钮禁用/启用防止重复操作,移除主线程中的
sleep避免UI卡顿。
内容的提问来源于stack exchange,提问作者韩云涛
相关产品推荐
相关产品推荐

