PyQtアプリケーションでのスレッド化:QtスレッドまたはPythonスレッドを使用しますか?


116

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

[はい、わかりました。今、2つの問題があります。]

とにかく、アプリケーションはPyQt4を使用しているので、Qtのスレッドを使用するthreadingか、Python モジュールを使用するのがよいでしょうか。それぞれの長所/短所は何ですか?それともまったく違う提案がありますか?

編集(報奨金):私の特定のケースでの解決策はおそらく、Jeff OberLukášLalinskýが提案するような非ブロッキングネットワークリクエストを使用することになるでしょう(したがって、基本的に同時実行性の問題はネットワークの実装に任せます)。一般的な質問に対する詳細な回答:

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


編集2:回答ありがとうございます。100%の合意はありませんが、答えは「use Qt」であるという広範なコンセンサスがあるようです。その利点は、他のライブラリとの統合であり、実際の欠点はありません。

2つのスレッドの実装の間で選択するために探している人のために、私は非常に彼らはPyQtはメーリングリストのスレッドを含め、ここで提供されるすべての答え、読んでお勧めアボットのリンクにします。

私が賞金について検討したいくつかの回答がありました。結局、私は非常に関連性の高い外部参照のために修道院長を選びました。しかし、それは危機一髪でした。

再度、感謝します。

回答:


106

これについては、PyQtメーリングリストでそれほど前に議論されていません。この主題に関するGiovanni Bajoのコメントを引用する:

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

一般的な経験則として、Qtと何らかの方法でやり取りする場合はQThreadsを使用し、そうでない場合はPythonスレッドを使用することになります。

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


2
良い答えですが、
ブロッククォート

2
QApplication.postEvent()を介してメインスレッドにイベントを投稿できず、そのためにQThreadが必要なのはなぜでしょうか。私はそれをしている人々を見たと思います、そしてそれはうまくいきました。
Trilarion

1
QCoreApplication.postEventクロスプラットフォームで実行され、数千時間テストされたアプリケーションで、毎秒100回の割合でPythonスレッドから呼び出しました。私はそれから問題を見たことがありません。宛先オブジェクトがMainThreadまたはQThreadにある限り、問題ないと思います。また、素敵なライブラリにまとめました。qtutilsを参照してください。
three_pineapples 2018

2
この質問と回答は非常に高く評価されているため、Pythonスレッドから特定のQtメソッドを使用しても安全な条件について詳しく説明した、ekhumoroによる最近のSOの回答を指摘する価値があると思います。これは、私と@Trilarionによって観察された観察された動作と一致します。
three_pineapples

33

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

編集:スレッドの詳細

Pythonスレッド

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

重要な注意:バイトコード命令の数はプログラムの行数に対応していないため、これは誤解を招く可能性があります。ミューテックスのロックが必要であるので、1つでも割り当ては、Pythonでアトミックではないかもしれない任意さえGILと、アトミックに実行されなければならないコードのブロック。

QTスレッド

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

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

結局、QTスレッドとPythonスレッドはどちらもシステムスレッドのラッパーです。Pythonスレッドは、Pythonで書かれていない(GILを暗黙的に使用している)部分は、どのような場合でもGILを使用するため、使用するのはわずかに安全です(ただし、上記の警告が引き続き適用されます)。

ノンブロッキングI / O

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

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

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

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


Re Twistedなど:実際のネットワーク機能を実行するサードパーティのライブラリを使用しています。私はそれにパッチをあてることを避けたいです。しかし、私はまだそれを調査します、ありがとう。
balpha 2009年

2
実際にGILをバイパスするものはありません。しかし、PythonはI / O操作中にGILを解放します。Pythonは、GILの取得/解放を担当するコンパイル済みモジュールに「引き渡す」ときにGILも解放します。
ジェフオーバー

2
アップデートは間違っています。Pythonコードは、PythonスレッドでもQThreadとまったく同じように実行されます。Pythonコードを実行するとGILを無罪にし(その後、Pythonがスレッド間の実行を管理します)、C ++コードを実行するとGILを解放します。全く違いはありません。
ルーカス・ラリンズキー

1
いいえ、要点は、どのようにスレッドを作成しても、Pythonインタープリターは気にしないということです。気になるのは、GILを取得でき、X命令の後でそれを解放/再取得できることです。たとえば、ctypesを使用して、別のスレッドで呼び出されるCライブラリからコールバックを作成できます。コードは、別のスレッドであることを知らなくても問題なく動作します。スレッドモジュールについては、特別なことは何もありません。
ルーカス・ラリンズキー

1
あなたは、ロックに関してQThreadがどのように異なるか、そして「コードをロックするために2つのロジックセットを管理する必要がある」と言っていました。私が言っているのは、それはまったく変わらないということです。ctypesとpthread_createを使用してスレッドを開始できます。これはまったく同じように機能します。Pythonコードは単にGILを気にする必要はありません。
ルーカス・ラリンズキー

21

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

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


1
Jeff Oberの回答について私がコメントしたこととは別に、QNetworkAccessManager有望に見えます。ありがとう。
balpha 2009年

13

PyTalkで働いていたときにも同じ質問をしました

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

シグナル/スロットエンジンを使用すると、スレッドから別のスレッドに、プロジェクトのすべての部分と通信できます。

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

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

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


9

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


5

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

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

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

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

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


7
ここで注意することが重要です:PyQt QThreads はグローバルインタープリターロックを取得します。 すべての PythonコードはGILをロックし、PyQtで実行するQThreadsはすべて Pythonコードを実行します。(そうでない場合、実際にはPyQtの「Py」部分を使用していません:)。そのPythonコードから外部Cライブラリに延期することを選択した場合、GILはリリースされますが、PythonスレッドとQtスレッドのどちらを使用しているかに関係なく当てはまります。
クォーク

それは私がすべてのPythonコードがロックを取ることを、伝えるためにしようとしたものを実際にだったが、それは別のスレッドで実行されているC / C ++コードの問題ではない
P_L

0

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


1
QNetworkAcessManagerスレッドやを必要としませんprocessEvents。非同期IO操作を使用します。
ルーカス・ラリンズキー

おっと...ええ、私はの組み合わせを使用していますQNetworkAcessManagerhttplib2。私の非同期コードはを使用していhttplib2ます。
brianz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.