web-dev-qa-db-ja.com

PyQtアプリケーションでのスレッド化:QtスレッドまたはPython threads?

Web接続を介して定期的にデータを取得するGUIアプリケーションを作成しています。この取得には時間がかかるため、取得プロセス中にUIが応答しなくなります(小さな部分に分割することはできません)。これが、Web接続を別のワーカースレッドにアウトソーシングしたい理由です。

[はい、私は知っています、今私は 2つの問題を抱えています 。]

とにかく、アプリケーションはPyQt4を使用するので、Qtのスレッドを使用するか、Python threadingモジュールを使用しますか?]の利点/欠点は何ですか?または、まったく異なる提案がありますか?

編集(報奨金):私の特定の場合の解決策は、おそらく Jeff Ober および LukášLalinský が提案したので(基本的に並行性の問題はネットワーク実装に任せます)、私はまだ一般的な質問に対するより詳細な答えが欲しいです:

ネイティブPythonスレッド(threadingモジュールから)]よりもPyQt4(つまりQt)のスレッドを使用する利点と欠点は何ですか?


編集2:回答ありがとうございます。 100%の合意はありませんが、答えは「use Qt」であるという幅広いコンセンサスがあるようです。なぜなら、その利点はライブラリの残りの部分との統合であり、実際の不利益は生じないからです。

2つのスレッド実装のいずれかを選択する場合は、 abbot がリンクするPyQtメーリングリストスレッドを含む、ここで提供されるすべての回答を読むことを強くお勧めします。

賞金について考えたいくつかの答えがありました。最終的に、非常に関連性の高い外部参照のためにアボットを選択しました。しかし、それは密接な呼び出しでした。

再度、感謝します。

104
balpha

これは、PyQtメーリングリストで 議論 あまり昔ではありませんでした。ジョバンニ・バホの引用 コメント 主題について:

ほとんど同じです。主な違いは、QThreadsがQtとよりよく統合されていることです(非同期信号/スロット、イベントループなど)。また、PythonスレッドからQtを使用することはできません(たとえば、QApplication.postEventを介してメインスレッドにイベントをポストすることはできません):そのためにはQThreadが必要です。

一般的な経験則では、Qtと何らかの形でやり取りする場合はQThreadsを使用し、それ以外の場合はPythonスレッドを使用します。

そして、PyQtの作者からのこの主題に関する以前のコメント:「どちらも同じネイティブスレッド実装のラッパーです」。そして、どちらの実装も同じ方法でGILを使用します。

99
abbot

Pythonのスレッドはよりシンプルで安全になり、I/Oベースのアプリケーション用であるため、GILをバイパスできます。とはいえ、Twistedまたはnon-blocking sockets/selectを使用した非ブロックI/Oを検討しましたか?

編集:スレッドの詳細

Pythonスレッド

Pythonのスレッドはシステムスレッドです。ただし、Pythonは、グローバルインタープリターロック(GIL)を使用して、インタープリターがバイトコード命令の特定サイズブロ​​ックのみを一度に実行するようにします。幸いなことに、Pythonは、入出力操作中にGILを解放し、スレッドを非ブロッキングI/Oのシミュレーションに役立ちます。

重要な警告:これは誤解を招く可能性があります。バイトコード命令の数はnotに対応しないためですプログラムの行数。 Pythonでは、単一の割り当てであってもアトミックではない場合があるため、GILを使用してもアトミックに実行する必要があるコードブロックanyにはミューテックスロックが必要です。

QTスレッド

Pythonがサードパーティのコンパイル済みモジュールに制御を渡すと、GILがリリースされます。必要に応じて原子性を確保するのはモジュールの責任になります。制御が戻されると、PythonはGILを使用します。これにより、サードパーティのライブラリをスレッドと組み合わせて使用​​すると混乱が生じます。外部のスレッドライブラリを使用するのはさらに困難です。モジュールとインタープリター。

QTスレッドは、リリースされたGILで動作します。 QTスレッドは、QTライブラリコード(およびGILを取得しないその他のコンパイル済みモジュールコード)を同時に実行できます。ただし、Python QTスレッドのコンテキスト内で実行されるコードstillはGILを取得し、管理する必要がありますtwoコードをロックするためのロジックのセット。

最終的に、QTスレッドとPythonスレッドはシステムスレッドのラッパーです。Pythonスレッドは、 Python(GILを暗黙的に使用))いずれの場合でもGILを使用します(ただし、上記の注意事項は引き続き適用されます)。

ノンブロッキングI/O

スレッドは、アプリケーションを非常に複雑にします。特に、Pythonインタープリターとコンパイルされたモジュールコードとの間のすでに複雑な相互作用を処理する場合。多くの場合、イベントベースのプログラミングを追跡するのは困難ですが、イベントベースの非ブロッキングI/Oは、スレッドよりも推論するのは難しくありません。

非同期I/Oを使用すると、開いている記述子ごとに、実行パスが一貫性があり、整然としていることを常に確認できます。明らかに、対処すべき問題があります。たとえば、あるオープンチャネルに依存するコードが、別のオープンチャネルがデータを返すときに呼び出されるコードの結果にさらに依存する場合の対処方法などです。

イベントベースのノンブロッキングI/Oの優れたソリューションの1つは、新しい Diesel ライブラリです。現在のところLinuxに制限されていますが、非常に高速で非常にエレガントです。

pyevent 、素晴らしいlibeventライブラリのラッパーを学ぶことも価値があります。これは、システムで利用可能な最速の方法(コンパイル時に決定)を使用してイベントベースのプログラミングの基本的なフレームワークを提供します。

32
Jeff Ober

QThreadの利点は、他のQtライブラリと統合されていることです。つまり、Qtのスレッド対応メソッドは、実行するスレッドを知る必要があり、スレッド間でオブジェクトを移動するには、QThreadを使用する必要があります。別の便利な機能は、スレッドで独自のイベントループを実行することです。

HTTPサーバーにアクセスしている場合は、QNetworkAccessManagerを考慮する必要があります。

21

PyTalk に取り組んでいたときに同じ質問を自問しました。

Qtを使用している場合は、QThreadを使用してQtフレームワーク、特にシグナル/スロットシステムを使用できるようにする必要があります。

シグナル/スロットエンジンを使用すると、スレッド間で、プロジェクトのすべての部分と会話できます。

さらに、どちらもC++バインディングであるため、この選択に関してパフォーマンス上の問題はあまりありません。

これが私のPyQtとスレッドの経験です。

QThreadを使用することをお勧めします。

13
Natim

ジェフには良い点がいくつかあります。 GUIの更新を実行できるメインスレッドは1つだけです。スレッド内からGUIを更新する必要がある場合、Qt-4の queued connection シグナルを使用すると、スレッド間でデータを簡単に送信でき、QThreadを使用している場合は自動的に呼び出されます。 Pythonスレッドを使用している場合、それらがどうなるかはわかりませんが、connect()にパラメーターを追加するのは簡単です。

9
Kaleb Pederson

どちらもお勧めできませんが、CPythonスレッドとQtスレッドの違いを説明してみることができます。

まず、CPythonスレッドは、少なくともPythonコードではありません。はい、各Pythonスレッド、ただし、現在、グローバルインタープリターロックを保持しているスレッドの実行が許可されます(C拡張機能とFFIコードがそれをバイパスする可能性がありますが、PythonバイトコードはスレッドがGILを保持していない間は実行されません)。

一方、Qtスレッドは基本的にシステムスレッド上の共通層であり、グローバルインタープリターロックを持たないため、同時に実行できます。 PyQtがそれをどのように扱うかはわかりませんが、QtスレッドがPythonコードを呼び出さない限り、それらは同時に実行できるはずです(さまざまな構造に実装される可能性のあるさまざまな追加ロックを禁止)。

追加の微調整のために、GILの所有権を切り替える前に解釈されるバイトコード命令の量を変更できます-値が低いほどコンテキストの切り替えが多くなり(応答性が高くなる可能性があります)、個々のスレッドごとのパフォーマンスが低下します(コンテキストスイッチにはコストがかかります-数回の命令ごとに切り替えてみてください。速度が上がりません。)

それがあなたの問題に役立つことを願っています:)

5
p_l

PythonとPyQtスレッドの正確な違いについてコメントすることはできませんが、QThreadQNetworkAcessManagerを使用して、あなたがやろうとしていることをやっています。スレッドが生きている間にQApplication.processEvents()を呼び出すことを確認しますGUIの応答性が本当に解決しようとしている問題である場合、 後の が役立ちます。

0
brianz