无法使用 QMutex 锁定变量
Posted
技术标签:
【中文标题】无法使用 QMutex 锁定变量【英文标题】:Can't lock variable with QMutex 【发布时间】:2021-01-13 07:44:56 【问题描述】:我是多线程的新手。我有一个 PySide2 应用程序。它有 3 个不同的线程。第一个是运行 GUI 的主线程,后者负责摄像头流,第三个负责提供 API 以从其他点访问 GUI 的 Flask。 (根据文章从不同线程访问 GUI 不是一个好主意:https://realpython.com/python-pyqt-qthread/#reusing-threads-qrunnable-and-qthreadpool)。
当请求到达端点时,我想在 GUI 上获取一些值并返回它们。我试图实现信号和槽机制来执行它。但是,我的端点在从 GUI 获取变量之前返回变量的默认值。负责端点的线程不会等待 GUI 线程设置将返回的值。我尝试以不同的方式使用 QMutex 锁定变量,以首先通过 GUI 设置值,但我无法成功。设置值后如何锁定变量并返回?
在线程上运行的 Flask 类:
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
SharedData 类:
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_())
【问题讨论】:
请提供minimal reproducible example 我添加了示例。 【参考方案1】:解决问题的方法是暂停 API(工作)线程。我仍然想知道我可以使用互斥锁、信号量等。
添加了 sleep 方法来暂停线程。
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_())
【讨论】:
以上是关于无法使用 QMutex 锁定变量的主要内容,如果未能解决你的问题,请参考以下文章