Python多线程:跨线程类间变量互相修改的实现方法求助
解决PyQt5多线程Worker类间变量互改问题
问题背景
现有基于PyQt5的多线程程序,包含Worker和Worker2两个QThread子类,需要实现两个类互相修改对方的变量,同时避免线程安全问题、不阻塞GUI。
解决方案
方法一:使用Qt信号与槽机制(推荐,符合Qt线程安全规范)
Qt的信号槽机制天然支持跨线程通信,会自动处理线程间的事件排队,避免直接访问线程对象变量带来的线程安全问题。通过给两个Worker类添加信号和槽,实现变量的间接修改。
修改后的核心代码:
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QApplication import sys import GUI import datetime import pyqtgraph import time plotsize = 20 class Worker2(QtCore.QThread): # 定义信号:通知Worker修改变量 update_worker_var = QtCore.pyqtSignal(int) # 自身变量 worker2_var = 0 def __init__(self, worker): super().__init__() print("Thread2 has been started") self.worker = worker # 连接Worker的信号到自身槽函数 self.worker.update_worker2_var.connect(self.set_worker2_var) def set_worker2_var(self, value): # 槽函数:修改自身变量 self.worker2_var = value print(f"Worker2 var updated to: {self.worker2_var}") def run(self): # 示例:每隔1秒通知Worker修改其变量 count = 0 while count < 10: QtCore.QThread.sleep(1) count += 1 self.update_worker_var.emit(count) # 读取Worker的变量(简单数值类型线程安全) print(f"Worker current var: {self.worker.worker_var}") class Worker(QtCore.QThread): progress = QtCore.pyqtSignal(int) finished = QtCore.pyqtSignal() widgetplot = QtCore.pyqtSignal(list, list) # 定义信号:通知Worker2修改变量 update_worker2_var = QtCore.pyqtSignal(int) # 自身变量 worker_var = 0 def __init__(self, plot_time, plot_value): super().__init__(objectName='WorkerThread') print("Thread has been started") self.plot_time = plot_time self.plot_value = plot_value # 保存Worker2的引用 self.worker2 = None def set_worker2(self, worker2): self.worker2 = worker2 # 连接Worker2的信号到自身槽函数 self.worker2.update_worker_var.connect(self.set_worker_var) def set_worker_var(self, value): # 槽函数:修改自身变量 self.worker_var = value print(f"Worker var updated to: {self.worker_var}") def run(self): _count = 0 _start_time = datetime.datetime.now() while 0 <= _count < 100: _count_prev = _count QtCore.QThread.usleep(10000) _diff = datetime.datetime.now() - _start_time _count = int((_diff.total_seconds() * 10)) if _count != _count_prev: print(_count) x = self.plot_time[:_count] y = self.plot_value[:_count] self.widgetplot.emit(x, y) # 示例:每5次计数通知Worker2修改变量 if _count % 5 == 0 and self.worker2: self.update_worker2_var.emit(_count) class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow): def __init__(self, parent=None): super(my_class, self).__init__(parent) self.setupUi(self) self.WidgetPlot.setXRange(0, 105, padding=0) self.WidgetPlot.setYRange(0, 105, padding=0) self.second_line = pyqtgraph.PlotDataItem(pen=pyqtgraph.mkPen('w', width=plotsize*2)) self.plot_time = [] self.plot_value = [] self.worker_thread = Worker(self.plot_time, self.plot_value) self.worker_thread.widgetplot.connect(self.update_second_line_plot) # 创建Worker2时传入Worker实例 self.worker_thread2 = Worker2(self.worker_thread) # 给Worker设置Worker2的引用 self.worker_thread.set_worker2(self.worker_thread2) self.pushButton.clicked.connect(self.my_function) self.WidgetPlot.setMouseEnabled(x=False, y=False) font=QtGui.QFont() font.setPixelSize(20) font.setBold(True) self.WidgetPlot.getAxis("bottom").setTickFont(font) self.WidgetPlot.getAxis("left").setTickFont(font) def my_function(self): _l = list(range(100)) self.plot_time.extend(_l) self.plot_value.extend(_l) self.start() def update_second_line_plot(self, plot_time, plot_value): self.second_line.setData(plot_time, plot_value) def start(self): self.WidgetPlot.plot(self.plot_time, self.plot_value, pen=pyqtgraph.mkPen('r', width=plotsize)) self.WidgetPlot.addItem(self.second_line) self.worker_thread.start() self.worker_thread2.start() def main(): app = QApplication(sys.argv) form = my_class() form.show() app.exec_() if __name__ == '__main__': main()
说明:
- 给每个Worker类添加信号用于通知对方修改变量,添加槽函数用于接收信号并修改自身变量
- 在主线程中完成两个Worker实例的互相引用传递,确保线程启动前已建立连接
- 跨线程的信号发送会自动加入目标线程的事件队列,无需手动处理线程同步,保证线程安全
方法二:使用线程安全的共享数据对象
如果需要共享复杂数据结构,可以创建一个带互斥锁的共享数据类,两个Worker通过这个类间接访问和修改变量,通过互斥锁保证线程安全。
核心代码示例:
class SharedData(QtCore.QObject): def __init__(self): super().__init__() self._mutex = QtCore.QMutex() # 存储两个Worker的变量 self.worker_var = 0 self.worker2_var = 0 def set_worker_var(self, value): # 加锁修改变量 locker = QtCore.QMutexLocker(self._mutex) self.worker_var = value def get_worker_var(self): locker = QtCore.QMutexLocker(self._mutex) return self.worker_var def set_worker2_var(self, value): locker = QtCore.QMutexLocker(self._mutex) self.worker2_var = value def get_worker2_var(self): locker = QtCore.QMutexLocker(self._mutex) return self.worker2_var class Worker2(QtCore.QThread): def __init__(self, shared_data): super().__init__() print("Thread2 has been started") self.shared_data = shared_data def run(self): count = 0 while count < 10: QtCore.QThread.sleep(1) count += 1 # 修改Worker的变量 self.shared_data.set_worker_var(count) # 读取Worker的变量 print(f"Worker current var: {self.shared_data.get_worker_var()}") # 修改自身对应的变量 self.shared_data.set_worker2_var(count * 2) class Worker(QtCore.QThread): progress = QtCore.pyqtSignal(int) finished = QtCore.pyqtSignal() widgetplot = QtCore.pyqtSignal(list, list) def __init__(self, plot_time, plot_value, shared_data): super().__init__(objectName='WorkerThread') print("Thread has been started") self.plot_time = plot_time self.plot_value = plot_value self.shared_data = shared_data def run(self): _count = 0 _start_time = datetime.datetime.now() while 0 <= _count < 100: _count_prev = _count QtCore.QThread.usleep(10000) _diff = datetime.datetime.now() - _start_time _count = int((_diff.total_seconds() * 10)) if _count != _count_prev: print(_count) x = self.plot_time[:_count] y = self.plot_value[:_count] self.widgetplot.emit(x, y) # 读取Worker2的变量 print(f"Worker2 current var: {self.shared_data.get_worker2_var()}") # 修改自身对应的变量 if _count % 5 == 0: self.shared_data.set_worker_var(_count) # 在my_class中初始化: class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow): def __init__(self, parent=None): super(my_class, self).__init__(parent) self.setupUi(self) # ...其他初始化代码... self.shared_data = SharedData() self.worker_thread = Worker(self.plot_time, self.plot_value, self.shared_data) self.worker_thread2 = Worker2(self.shared_data) # ...其他代码...
说明:
SharedData类使用QMutex和QMutexLocker保证变量读写的原子性,避免多线程竞争- 两个Worker通过共享这个数据对象来间接修改对方的变量,所有变量操作都通过加锁的方法完成
- 适合共享复杂数据结构或需要频繁读写的场景
内容的提问来源于stack exchange,提问作者AndrewFFL
相关产品推荐
相关产品推荐

