关于从QThread内部调用QThread外部的方法,有几个问题——我的设计有缺陷吗



我有一个应用程序,它有一个GUI线程和许多不同的工作线程。在这个应用程序中,我有一个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变为True之外,等待用户输入文本的更好方法,请告诉我)。solveAlternateComputation是同一类中另一个自动解决计算的方法,但它也调用了设置结果文本的setResultText()方法。

第二个信号也会更新主GUI的状态栏文本。

更糟糕的是,我认为上面的设计虽然可能有缺陷,但不是问题所在。

我相信,问题在于我对这些方法的调用方式,而这些方法来自工作线程(注意,我有多个类似的工作线程,它们都是不同的"平台")

假设我有这个(而且我有):

class WorkerPlatform1(QThread):
#Init and other methods are here
def run(self):
#Thread does its job here, but then when it needs to present the  
#computation, instead of emitting a signal, this is what I do
self.f = functions.Functions
result = self.f.solveComputation(platform, computation)
if result:
#Go on with the task
else:
self.f.updateCurrentStatus(platform, "Error grabbing computation!")

在这种情况下,我认为我的缺陷是线程本身没有发出任何信号,而是直接调用驻留在该线程外部的可调用程序。我认为这可能会导致我的应用程序崩溃,这是对的吗?尽管故障模块报告为QtGuid4.dll

还有一件事:Functions类中的这两个方法几乎同时被多个线程访问。这是否可取——让多个线程同时访问驻留在线程外部的方法?我会"混淆"我的程序吗?我之所以这么问,是因为那些说应用程序没有崩溃的人报告说,solveComputation()经常返回不正确的文本——不是所有时间,而是经常返回。由于COMPUTATION_URL的服务器可能需要一些时间(甚至10秒以上)来响应,那么,一旦一个线程调用该方法,而urllib库仍在等待服务器响应,那么在这段时间内,另一个线程可以调用它,导致它使用不同的COMPUTATION_URL,这在某些情况下会导致它返回错误的值吗?

最后,我正在考虑解决方案:对于我的第一个(崩溃)问题,你认为正确的解决方案是直接从线程本身发出Signal,然后在GUI线程中连接它吗?这样做对吗?

其次,对于返回错误值的solveComputation,我是否可以通过将该方法(以及伴随的方法)移动到每个Worker类来解决它?然后我可以直接调用它们,希望每个线程都有正确的响应——或者几十个不同的响应(因为我有那么多线程)?

谢谢大家,我为文字墙道歉。

EDIT:我想补充一点,当与一些用户在控制台中运行时,此错误显示为QObject: Cannot create children for a parent that is in a different thread. (Parent is QLabel(0x4795500), parent's thread is QThread(0x2d3fd90), current thread is WordpressCreator(0x49f0548)

如果你真的像这样使用Functions类,并且类方法将结果存储在类属性上,并在多个工作人员之间共享,那么你的设计就是有缺陷的。它应该使用所有的实例方法,并且每个线程都应该使用这个类的一个实例:

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,您可以使用QNetworkAccessManager发出请求,并在结果准备好时使用信号进行通知,而不是通过睡眠来检查何时准备好。

最新更新