あなたが直接的な解決策を与えられている間、私はあなたの最初の質問に答えて、あなたのコードが期待通りに機能しない理由を説明しようとします。
開示:私は現在Eventletを維持しています。このコメントには、妥当なサイズに収まるようにいくつかの簡略化が含まれます。
協調マルチスレッドの簡単な紹介
マルチスレッドを実行するには2つの方法があり、Eventletは協調的なアプローチを利用します。コアとなるのは、基本的に独立した「実行コンテキスト」を作成できるGreenletライブラリです。すべてのローカル変数のフリーズ状態や次の命令へのポインタなどのコンテキストを考えることができます。基本的に、マルチスレッド=コンテキスト+スケジューラ。Greenletはコンテキストを提供するため、スケジューラーが必要です。スケジューラーは、現在CPUを占有するコンテキストを決定するものです。決定を下すには、コードも実行する必要があります。これは、別のコンテキスト(グリーンスレッド)を意味します。この特別なグリーンスレッドは、Eventletコードベースではハブと呼ばれます。スケジューラーは順序集合を維持しますできるだけ早く実行する必要のあるコンテキストの数-キューと、何か(ネットワークIOや時間制限のあるスリープなど)の終了を待機しているコンテキストのセットを実行します。
ただし、協調マルチタスクを実行しているため、明示的に別のコンテキストに譲らない限り、あるコンテキストは無期限に実行されます。これは非常に悲しいプログラミングスタイルであり、定義上、既存のライブラリと互換性がありません(they-know-whoを指しています)。したがって、Eventletが行うことは、すべてをブロックするのではなくハブに切り替えるように変更された、共通モジュールのグリーンバージョンを提供することです。次に、他のグリーンスレッドまたはHubの外部イベント待機の実装に時間がかかる場合があります。その場合、Hubはそのイベントを生成するグリーンスレッドに戻り、実行を続行します。
終わり。ここで問題に戻ります。
eventlet.spawn
実際に行うこと:新しい実行コンテキストを作成します。基本的に、オブジェクトをメモリに割り当てます。また、このコンテキストを実行キューに入れるようにスケジューラーに指示するため、最初の可能な瞬間に、ハブは新しく生成された関数に切り替わります。あなたのコードはそのような瞬間を提供しません。他のグリーンスレッドへの実行を明示的に放棄する場所はありません。Eventletの場合、これは通常、を介して実行されeventlet.sleep()
ます。また、一般的なモジュールのグリーンバージョンを使用しないため、他のコードが待機しているときに暗黙的に生成される可能性はありません。最も適切な(唯一ではないにしても)場所は、WSGIサーバーのacceptループです。これにより、他のグリーンスレッドが次の要求を待機している間に実行される機会が与えられます。最初の回答で言及eventlet.monkey_patch()
これは、すべての(またはサブセットの)共通モジュールを対応するグリーンバージョンに置き換えるための便利な方法です。
全体的なデザインに関する不要な意見
別のセクションで、簡単にスキップします。エラー耐性のあるソフトウェアを構築している場合は、通常、生成されたスレッド(「グリーン」を含むがこれに限定されない)とプロセスの実行時間を制限し、少なくともそれらの未処理のエラーを報告(ログ)または対応する必要があります。提供されたコードでは、生成されたグリーンスレッドは、技術的には次の瞬間または5分後に実行されるか(CPUを譲る人がいないため)、未処理の例外で失敗する可能性があります。幸いなことに、Eventletは両方の問題に対して2つの解決策を提供します。タイムアウト with_timeout()により、待機時間を制限できます(生成されない場合は、制限できない可能性があります)。GreenThread.link()すべての例外をキャッチします。「メイン」コードで例外を再発生させたくなるかもしれませんが(私にとっては)、link()
それを簡単に許可しますが、例外はスリープとIO呼び出し(ハブに譲る場所)から発生することを考慮してください。これにより、直感に反するトレースバックが提供される場合があります。