无法使用 QMutex 锁定变量

Can't lock variable with QMutex

我是多线程方面的新手。我有一个 PySide2 应用程序。它有 3 个不同的线程。第一个是运行 GUI 的主线程,后者负责相机流,第三个负责提供 API 从其他点访问 GUI 的 Flask。 (根据文章从不同线程访问 GUI 不是一个好主意:https://realpython.com/python-pyqt-qthread/#reusing-threads-qrunnable-and-qthreadpool)。

当请求到达端点时,我想在 GUI 上获取一些值并 return 它们。我试图实现信号和槽机制来执行它。但是,在我从 GUI 获取变量之前,我的端点 returns 是变量的默认值。负责端点的线程不会等待 GUI 线程设置将被 returned 的值。我尝试以不同的方式使用 QMutex 锁定变量,首先通过 GUI 设置值,但我无法成功。如何在设置值后锁定变量和return?

在线程上运行的 Flask class:

from PySide2.QtCore import Signal, QObject, Slot, QMutex
from flask import Flask

from SharedData import SharedData

class API(QObject):
    signal_get_value = Signal()

    def __init__(self):
        super(API, self).__init__()
        self.app = Flask(__name__)
        self.app.add_url_rule('/get_value/', 'get_value', self.get_value)
        self.data = SharedData()


    def start(self):
        self.app.run()

    def get_value(self):
        self.signal_get_value.emit()
        measurement = self.data.get_measurement()
        data = {"measurement": measurement}

        return data

共享数据class:

class SharedData:
    __measurement = None

    @classmethod
    def get_measurement(cls):
        return cls.__measurement

    @classmethod
    def set_measurement(cls, measurement):
        cls.__measurement = measurement

我的GUI模块中修改值的方法:

mutex = QMutex()

    def read_measurement(self):
        self.mutex.lock()
        
        #some processes and the output assign to the 'result' variable
        
        self.api.data.set_measurement(result)
        self.mutex.unlock()

编辑

我正在添加一个代码来澄清它。您可以在其中找到 read_measurement 方法。其余代码相同。 @eyllanesc

import sys

from PySide2.QtCore import QThread, QMutex
from PySide2.QtGui import QPixmap
from PySide2.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton, QHBoxLayout, QLabel

from API import API

class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.init_UI()


    def init_UI(self):
        self.setFixedSize(690, 530)
        self.image_lbl = QLabel()

        self.image_lbl.setPixmap(QPixmap("img/im.jpg"))

        btn_cnt = QPushButton("Continue")
        btn_pa = QPushButton("Pause")

        hbox = QHBoxLayout()
        hbox.addWidget(btn_cnt)
        hbox.addWidget(btn_pa)

        vbox = QVBoxLayout()
        vbox.addWidget(self.image_lbl)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.start_api()
        self.show()


    def start_api(self):
        self.thread_API = QThread()
        self.api = API()
        self.api.moveToThread(self.thread_API)
        self.thread_API.started.connect(self.api.start)
        self.api.signal_get_value.connect(self.read_measurement)

        self.thread_API.start()

    dv = 0
    mutex = QMutex()

    def read_measurement(self):
        self.mutex.lock()

        measurement = None
        result = "measurement " + str(self.dv)
        self.dv += 1
        self.api.data.set_measurement(result)

        self.mutex.unlock()



if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_form = MainWindow()
    sys.exit(app.exec_())

解决该问题的方法是暂停 API(worker) 线程。我仍然想知道我是否可以使用互斥量、信号量等。

添加了暂停线程的睡眠方法。

from time import sleep

from PySide2.QtCore import Signal, QObject, Slot, QMutex
from flask import Flask

from SharedData import SharedData

class API(QObject):
    signal_get_value = Signal()

    def __init__(self):
        super(API, self).__init__()
        self.__app = Flask(__name__)
        self.__app.add_url_rule('/get_value/', 'get_value', self.get_value)
        self.data = SharedData()
        self.pause = True


    def start(self):
        self.__app.run()

    def __sleep_thread(self):
        """method pauses the thread to enable other threads to get the request done 

        Returns
        -------
        None
        """

        while self.pause:
            sleep(0.05)

        self.pause = True


    def get_value(self):
        self.signal_get_value.emit()
        self.__sleep_thread()

        measurement = self.data.get_measurement()
        data = {"measurement": measurement}

        return data

GUI 模块。

import sys

from PySide2.QtCore import QThread, QMutex
from PySide2.QtGui import QPixmap
from PySide2.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton, QHBoxLayout, QLabel

from API import API

class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.init_UI()


    def init_UI(self):
        self.setFixedSize(690, 530)
        self.image_lbl = QLabel()

        self.image_lbl.setPixmap(QPixmap("img/im.jpg"))

        btn_cnt = QPushButton("Continue")
        btn_pa = QPushButton("Pause")

        hbox = QHBoxLayout()
        hbox.addWidget(btn_cnt)
        hbox.addWidget(btn_pa)

        vbox = QVBoxLayout()
        vbox.addWidget(self.image_lbl)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.start_api()
        self.show()


    def start_api(self):
        self.thread_API = QThread()
        self.api = API()
        self.api.moveToThread(self.thread_API)
        self.thread_API.started.connect(self.api.start)
        self.api.signal_get_value.connect(self.read_measurement)

        self.thread_API.start()
        # ToDo: Do I need the terminate thread when exited from the app?

    dv = 0
    def read_measurement(self):
        measurement = None
        result = "measurement " + str(self.dv)
        self.dv += 1
        self.api.data.set_measurement(result)
        self.api.pause = False



if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_form = MainWindow()
    sys.exit(app.exec_())