You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:05:18