Python 关于从QThread内部调用QThread外部方法的几个问题-我的设计有缺陷吗?
我有一个应用程序,它有一个GUI线程和许多不同的工作线程。在这个应用程序中,我有一个Python 关于从QThread内部调用QThread外部方法的几个问题-我的设计有缺陷吗?,python,multithreading,qt,pyqt,Python,Multithreading,Qt,Pyqt,我有一个应用程序,它有一个GUI线程和许多不同的工作线程。在这个应用程序中,我有一个functions.py模块,它包含许多不同的“实用”函数,这些函数在整个应用程序中都使用 昨天,应用程序已经发布,一些用户(少数,但仍然)报告了应用程序崩溃的问题。我查看了我的代码,发现了一个可能的设计缺陷,我想和SO的可爱的人们一起检查一下,看看我是否正确,这是否真的是一个缺陷 假设我在我的functions.py模块中定义了这个: class Functions: solveComputation
functions.py
模块,它包含许多不同的“实用”函数,这些函数在整个应用程序中都使用
昨天,应用程序已经发布,一些用户(少数,但仍然)报告了应用程序崩溃的问题。我查看了我的代码,发现了一个可能的设计缺陷,我想和SO的可爱的人们一起检查一下,看看我是否正确,这是否真的是一个缺陷
假设我在我的functions.py
模块中定义了这个:
class Functions:
solveComputationSignal = Signal(str)
updateStatusSignal = Signal(int, str)
text = None
@classmethod
def setResultText(self, text):
self.text = text
@classmethod
def solveComputation(cls, platform, computation, param=None):
#Not the entirety of the method is listed here
result = urllib.urlopen(COMPUTATION_URL).read()
if param is None:
cls.solveComputationSignal.emit(result)
else:
cls.solveAlternateComputation(platform, computation)
while not self.text:
time.sleep(3)
return self.text if self.text else False
@classmethod
def updateCurrentStatus(cls, platform, statusText):
cls.updateStatusSignal.emit(platform, statusText)
我认为这些方法本身是好的。这里定义的两个信号在GUI线程中连接到。第一个信号弹出一个对话框,其中显示计算。GUI线程调用setResultText()
方法并将结果字符串设置为用户输入的字符串(如果有人知道更好的方法等待用户输入文本,而不是休眠并等待self.text
变为真,请告诉我)。solveAlternateCompution
是同一类中自动解决计算的另一个方法,但是,它也调用设置结果文本的setResultText()
方法
第二个信号也会更新主GUI的状态栏文本
更糟糕的是,我认为上述设计虽然可能有缺陷,但不是问题所在
我相信,问题在于我调用这些方法的方式,它们来自工作线程(请注意,我有多个类似的工作线程,它们都是不同的“平台”)
假设我有这个(我有):
在本例中,我认为我的缺陷在于线程本身没有发出任何信号,而是直接调用驻留在该线程之外的可调用函数。我认为这会导致我的应用程序崩溃,对吗?尽管故障模块报告为QtGui4.dll
还有一件事:函数
类中的这两个方法几乎同时被多个线程访问。这是否是明智的——多个线程是否可以同时访问驻留在线程外部的方法?我会不会“混淆”我的程序?我问这个问题的原因是,那些说应用程序没有崩溃的人报告说,solveComputation()
经常返回不正确的文本——不是所有时间,而是经常返回。由于COMPUTATION\u URL
的服务器可能需要一些时间来响应(甚至10秒以上),因此,一旦一个线程调用该方法,而urllib
库仍在等待服务器响应,那么在这段时间内另一个线程可以调用它,导致它使用不同的COMPUTATION\u URL
,这将导致它在某些情况下返回不正确的值
最后,我想到了解决方案:对于我的第一个(崩溃)问题,您认为正确的解决方案是直接从线程本身发出信号,然后在GUI线程中连接它吗?这样做对吗
其次,对于返回不正确值的solveComputation
,我是否会通过将该方法(以及伴随的方法)移动到每个工作者
类来解决它?然后我可以直接给他们打电话,希望每个线程都有正确的响应——或者几十个不同的响应(因为我有那么多线程)——是吗
谢谢大家,我为文字墙道歉
编辑:我想补充一点,当在控制台中与一些用户一起运行时,会出现此错误QObject:无法为位于不同线程中的父线程创建子线程。
(父线程为QLabel(0x4795500),父线程为QThread(0x2d3fd90),当前线程为WordpressCreator(0x49f0548)
如果您真的像这样使用函数
类,将结果存储在类属性上,并在多个工作线程之间共享,那么您的设计就是有缺陷的。它应该使用所有实例方法,每个线程都应该使用此类的一个实例:
class Functions(QObject):
solveComputationSignal = pyqtSignal(str)
updateStatusSignal = pyqtSignal(int, str)
def __init__(self, parent=None):
super(Functions, self).__init__(parent)
self.text = ""
def setResultText(self, text):
self.text = text
def solveComputation(self, platform, computation, param=None):
result = urllib.urlopen(COMPUTATION_URL).read()
if param is None:
self.solveComputationSignal.emit(result)
else:
self.solveAlternateComputation(platform, computation)
while not self.text:
time.sleep(3)
return self.text if self.text else False
def updateCurrentStatus(self, platform, statusText):
self.updateStatusSignal.emit(platform, statusText)
# worker_A
def run(self):
...
f = Functions()
# worker_B
def run(self):
...
f = Functions()
另外,对于执行urlopen
,您可以使用发出请求并在结果准备好时使用通知信号,而不是通过睡眠来检查结果是否准备就绪。您正在使用具有类属性的classmethod来存储结果,并在线程之间共享该类。我可以看到c需要改进。是的。我知道它可以改进,但您愿意详细说明如何改进吗?更好的方法是什么?如果您的函数
类需要将结果存储为成员,请不要使用classmethods。将它们都设为普通方法,并在\uuuu init\uuuuu
中设置您的实例属性,否则每个工作人员都会共享这些方法s将损坏彼此的结果,对于您的编辑,您在线程中使用QLabel
做什么。您正在创建它们吗?我已经修复了编辑-不知道如果QThread调用sys.excepthook
(或任何其他方法),它将在该线程的上下文中调用它,而不是在定义它的线程中调用它(该线程是主GUI线程)。非常感谢您的回答。这可能是报告了如此多错误计算的原因,因为类变量被覆盖了-我没有想到。我的函数类还有许多其他类方法,我无法摆脱,因此我想是时候创建一个新类了,特别是针对t这些计算。类方法没有问题。你仍然可以用self从实例方法调用它们。关键是要知道数据何时共享。类方法适用于那些不需要唯一实例来操作的方法。它们不需要
class Functions(QObject):
solveComputationSignal = pyqtSignal(str)
updateStatusSignal = pyqtSignal(int, str)
def __init__(self, parent=None):
super(Functions, self).__init__(parent)
self.text = ""
def setResultText(self, text):
self.text = text
def solveComputation(self, platform, computation, param=None):
result = urllib.urlopen(COMPUTATION_URL).read()
if param is None:
self.solveComputationSignal.emit(result)
else:
self.solveAlternateComputation(platform, computation)
while not self.text:
time.sleep(3)
return self.text if self.text else False
def updateCurrentStatus(self, platform, statusText):
self.updateStatusSignal.emit(platform, statusText)
# worker_A
def run(self):
...
f = Functions()
# worker_B
def run(self):
...
f = Functions()