PyQt5 QThread未并行执行:工作线程与GUI线程ID一致问题排查
问题描述
尝试为侧边QPushButton加载缩略图,计划为每张图片创建一个QThread实现并行加载,但日志显示所有Worker的线程ID完全相同,未实现预期的并行执行(CPU为4核8线程)。
原始代码
class Worker(QObject): finished = pyqtSignal(QIcon) def run(self, path: str): import threading logging.debug(f'Worker started at {(t1 := time.time())} seconds on thread {threading.get_ident()}') icon = QIcon icon.addFile(path) logging.debug(f'Worker finished @ {time.time()} seconds, {(time.time() - t1):.3} seconds later') self.finished.emit(icon) # 创建线程与Worker的逻辑 self.thread = QThread() self.worker = Worker() self.worker.moveToThread(self.thread) # 连接信号槽 self.thread.started.connect(lambda: self.worker.run(self.thumbnail_path)) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.set_icon) # 实际为 lambda icon: a_btn.setIcon(icon) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) # 启动线程 logging.debug(f'Thread started at {(t1 := time.time())} seconds') self.thread.start(priority=0)
日志输出
2023-05-14T13:27:04 Thread started at 1684063624.1749578 seconds 2023-05-14T13:27:04 Thread started at 1684063624.3233523 seconds 2023-05-14T13:27:04 Thread started at 1684063624.3353844 seconds 2023-05-14T13:27:04 Thread started at 1684063624.5469468 seconds 2023-05-14T13:27:04 Thread started at 1684063624.7655282 seconds 2023-05-14T13:27:06 Widget setup completed at 1684063626.6284823 seconds 2023-05-14T13:27:06 Worker started at 1684063626.6485348 seconds on thread 10100 2023-05-14T13:27:06 Worker finished @ 1684063626.6585634 seconds, 0.01 seconds later 2023-05-14T13:27:06 Worker started at 1684063626.6585634 seconds on thread 10100 2023-05-14T13:27:06 Worker finished @ 1684063626.8139749 seconds, 0.155 seconds later 2023-05-14T13:27:06 Worker started at 1684063626.814978 seconds on thread 10100 2023-05-14T13:27:06 Worker finished @ 1684063626.8250043 seconds, 0.01 seconds later 2023-05-14T13:27:06 Worker started at 1684063626.826007 seconds on thread 10100 2023-05-14T13:27:07 Worker finished @ 1684063627.0385983 seconds, 0.213 seconds later 2023-05-14T13:27:07 Worker started at 1684063627.0395803 seconds on thread 10100 2023-05-14T13:27:07 Worker finished @ 1684063627.2581596 seconds, 0.219 seconds later
环境:PyCharm 2022专业版、Python 3.11、Windows 10;待加载文件大小300KB至50MB,存储在7200RPM HDD中。
原因分析
直接调用Worker.run方法未触发线程切换
代码中self.thread.started.connect(lambda: self.worker.run(self.thumbnail_path))是通过lambda直接调用worker.run(),这种方式属于同步调用,会在主线程中执行run方法,而非切换到子线程。Qt的跨线程执行依赖信号槽的自动调度:只有当信号连接到Worker的槽函数时,槽函数才会在Worker所在的子线程执行,直接调用方法不会触发线程上下文切换。QIcon初始化错误
代码中icon = QIcon是将QIcon类赋值给变量,而非创建QIcon实例,后续调用icon.addFile(path)实际是调用类方法,会导致功能异常,但这并非线程ID相同的直接原因。
修复方案
方案1:通过信号触发Worker执行
修改Worker类,新增启动信号,通过信号传递图片路径,确保run方法在子线程执行:
class Worker(QObject): finished = pyqtSignal(QIcon) start_task = pyqtSignal(str) # 新增启动任务的信号,传递图片路径 def __init__(self): super().__init__() self.start_task.connect(self.run) # 将启动信号连接到run方法 def run(self, path: str): import threading logging.debug(f'Worker started at {(t1 := time.time())} seconds on thread {threading.get_ident()}') icon = QIcon() # 修复:创建QIcon实例 icon.addFile(path) logging.debug(f'Worker finished @ {time.time()} seconds, {(time.time() - t1):.3} seconds later') self.finished.emit(icon) # 创建线程与Worker的逻辑修改 self.thread = QThread() self.worker = Worker() self.worker.moveToThread(self.thread) # 信号槽连接调整 self.thread.started.connect(lambda: self.worker.start_task.emit(self.thumbnail_path)) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.set_icon) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) # 启动线程 logging.debug(f'Thread started at {(t1 := time.time())} seconds') self.thread.start(priority=0)
方案2:将图片路径作为Worker属性
如果不需要动态传递路径,也可以在Worker初始化时传入路径,避免lambda的上下文问题:
class Worker(QObject): finished = pyqtSignal(QIcon) def __init__(self, path: str): super().__init__() self.path = path def run(self): import threading logging.debug(f'Worker started at {(t1 := time.time())} seconds on thread {threading.get_ident()}') icon = QIcon() icon.addFile(self.path) logging.debug(f'Worker finished @ {time.time()} seconds, {(time.time() - t1):.3} seconds later') self.finished.emit(icon) # 创建线程与Worker的逻辑修改 self.thread = QThread() self.worker = Worker(self.thumbnail_path) self.worker.moveToThread(self.thread) # 信号槽连接调整 self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.set_icon) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) # 启动线程 logging.debug(f'Thread started at {(t1 := time.time())} seconds') self.thread.start(priority=0)
内容的提问来源于stack exchange,提问作者An average guy
相关产品推荐
相关产品推荐

