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)

これらの方法自体は問題ないと思います。ここで定義された 2 つのシグナルは、GUI スレッドで接続されます。最初の信号は、計算が表示されるダイアログをポップアップ表示します。GUI スレッドはメソッドを呼び出し、結果の文字列をユーザーが入力したとおりに設定します (スリープして True になるsetResultText()のを待つ以外に、ユーザーがテキストを入力するまで待機するより良い方法を誰かが知っている場合は、私に知らせてください)。self.textsolveAlternateComputation、計算を自動的に解決する同じクラスの別のメソッドsetResultText()ですが、結果のテキストを設定するメソッドも呼び出します。

2 番目のシグナルは、メイン GUI の statusBar テキストも更新します。

さらに悪いことに、上記の設計にはおそらく欠陥がありますが、問題ではないと思います。

問題は、私がこれらのメソッドを呼び出す方法にあると私は信じています。これはワーカースレッドからのものです(複数の同様のワーカーがあり、それらはすべて異なる「プラットフォーム」であることに注意してください)

私がこれを持っていると仮定します(そして私は持っています):

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 として報告されていますが、

もう 1 つ: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複数のワーカー間で共有されているクラス属性に結果を格納するclassmethodsでこのようなクラスを実際に使用している場合、設計に欠陥があります。すべてのインスタンスメソッドを使用する必要があり、各スレッドは次のクラスのインスタンスを使用する必要があります。

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てリクエストを作成し、シグナルを使用して結果の準備ができたときに通知を受け取ることができます。

于 2012-12-03T02:43:04.050 に答える