0

我有一个具有 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!")

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

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

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

其次,对于solveComputation返回不正确的值,我会通过将该方法(和随附的方法)移动到每个Worker类来解决它吗?那么我可以直接给他们打电话,并希望每个线程都有正确的响应——或者,几十个不同的响应(因为我有那么多线程)?

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

编辑:我想补充一点,当与某些用户在控制台中运行时,会出现此错误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)

4

1 回答 1

1

如果您真的将这样的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,而不是做 sleep 来检查它什么时候准备好,你可以利用QNetworkAccessManager来提出你的请求并使用信号在结果准备好时得到通知。

于 2012-12-03T02:43:04.050 回答