4

Twisted のドキュメンテーションは、リアクターがカバーの下でこれをエレガントに処理するという同じアプリケーションで と のreactor.spawnProcess()ようなテクニックを組み合わせても問題ないと私に信じさせました。threads.deferToThread()実際に試してみると、アプリケーションがデッドロックすることがわかりました。複数のスレッドを単独で使用するか、子プロセスを単独で使用すると、すべて問題ありません。

リアクターのソースを調べると、実行中の可能性のある複数のスレッドを考慮せずにSelectReactor.spawnProcess()メソッドが単純に呼び出されていることがわかりました。あなたへの呼び出しから始まると、複数の同時スレッドが実行され、同じファイル記述子で何を知っているかを知っている2つのプロセスがos.fork()あるため、これはデッドロックを説明しています。os.fork()

SO に対する私の質問は、この問題を解決するための最善の戦略は何ですか?

私が念頭に置いているのは、 をサブクラスSelectReactor化することです。これにより、シングルトンになりos.fork()、インスタンス化されたときにすぐに 1 回だけ呼び出されます。子プロセスはバックグラウンドで実行され、親のサーバーとして機能します (パイプを介したオブジェクトのシリアル化を使用してやり取りします)。親は引き続きアプリケーションを実行し、必要に応じてスレッドを使用できます。親での呼び出しspawnProcess()は子プロセスに委譲されます。子プロセスは 1 つのスレッドのみを実行することが保証されるため、os.fork()安全に呼び出すことができます。

誰もこれを以前にやったことがありますか?もっと速い方法はありますか?

4

4 に答える 4

4

この問題を解決するための最善の戦略は何ですか?

問題を説明するチケットを(おそらく登録後に) 提出し、できれば再現可能なテスト ケースを (最大の精度のために) 使用します。次に、それを実装するための最良の方法 (または方法 - 異なるプラットフォームでは異なるソリューションが必要になる場合があります) について、いくつかの議論が行われる可能性があります。

子プロセスのリープに関連するパフォーマンスの問題を解決するために、子プロセスをすぐに作成してさらに子プロセスを作成するというアイデアは以前に提起されました。そのアプローチで 2 つの問題が解決されれば、もう少し魅力的に見え始めます。このアプローチの潜在的な問題の 1 つはspawnProcess、子の PID を提供し、シグナルの送信を許可するオブジェクトを同期的に返すことです。途中に中間プロセスがある場合は、PID を返す前にメイン プロセスに戻す必要があるため、これを実装するにはもう少し作業が必要spawnProcessです。childFDs子プロセスでファイル記述子を単に継承することはもはや不可能になるため、同様の課題が議論をサポートします。

別の解決策 (ややハック的かもしれませんが、実装上の課題も少ないかもしれません) は、 を呼び出すsys.setcheckinterval前に非常に大きな数で呼び出してos.forkから、親プロセスのみで元のチェック間隔を復元することです。os.execvpeこれは、すべての余分なスレッドを破棄するまで、プロセス内のスレッドの切り替えを回避するのに十分なはずです。特定のリソース (ミューテックスや条件など) が悪い状態のままになるため、これは完全に正しいとは言えませんが、これらを使用することdeferToThreadはあまり一般的ではないため、ケースには影響しない可能性があります。

于 2011-05-19T03:59:15.917 に答える
2

しばらくしてこの問題に戻ると、これを行うと次のことがわかりました。

reactor.callFromThread(reactor.spawnProcess, *spawnargs)

これの代わりに:

reactor.spawnProcess(*spawnargs)

その後、私の小さなテストケースで問題が解決します。Twisted のドキュメント「Using Processes」には、「Twisted のほとんどのコードはスレッドセーフではありません。たとえば、プロトコルからトランスポートへのデータの書き込みはスレッドセーフではありません。」というコメントがあります。

Jean-Paul が言及したこの問題を抱えている他の人々も、同様の間違いを犯している可能性があると思います。リアクターやその他の API 呼び出しが正しいスレッド内で行われるようにする責任は、アプリケーションにあります。そして明らかに、非常に狭い例外を除いて、「正しいスレッド」はほとんどの場合、メインのリアクター スレッドです。

于 2011-08-24T19:42:58.923 に答える
2

Jean-Paul が彼の回答で与えたアドバイスは良いものですが、これはうまくいくはずです (ほとんどの場合はうまくいきます)。

まず、Twisted はホスト名の解決にもスレッドを使用します。クライアント接続も行う Twisted プロセスでサブプロセスを使用したことは間違いありません。したがって、これは実際に機能します。

次に、fork()子プロセスに複数のスレッドを作成しません。 を記述する規格によるとfork()

プロセスは単一のスレッドで作成されます。マルチスレッド プロセスが fork() を呼び出す場合、新しいプロセスには呼び出しスレッドのレプリカが含まれます。

これは、潜在的なマルチスレッドの問題がないと言っているわけではありませんspawnProcess。標準には次のようにも書かれています。

...エラーを回避するために、子プロセスは、exec関数の1つが呼び出されるまで、非同期シグナルセーフ操作のみを実行できます...

非同期シグナルセーフ操作のみが使用されることを保証するものは何もないと思います。

したがって、スレッドが複製されるサブプロセスではないため、正確な問題についてより具体的に説明してください。

于 2011-05-19T07:49:21.567 に答える
1

Linux で fork() を実行すると、子プロセスのスレッドは 1 つだけになります。

Twisted でスレッドを使用する場合、スレッドが呼び出しを許可されている唯一の Twisted API は callFromThread であることに気付いていると思いますか? 他のすべての Twisted API は、メインのリアクター スレッドからのみ呼び出す必要があります。

于 2011-06-27T15:20:06.077 に答える