この目的で使用されるジェネレーターは、多くの場合 (他の多くの用語の中でも)タスクと呼ばれます。ここでは、わかりやすくするためにその用語を使用します。はい、可能です。実際、状況によっては機能し、理にかなっているアプローチがいくつかあります。ただし、(私が認識している) は、 と の少なくとも 1 つに相当するものなしでは機能しませgevent.spawn
んgevent.joinall
。より強力で適切に設計されたものには、両方に相当するものが必要です。
基本的な問題は次のとおりです。ジェネレーターは ( にヒットしたときに) 中断される可能性がありますyield
が、それだけです。それらを再び開始するには、next()
それらを呼び出す他のコードが必要です。実際、最初から何かnext()
を行うには、新しく作成されたジェネレーターを呼び出す必要さえあります。同様に、ジェネレーター自体は、次に何を実行するかを決定するのに最適な場所ではありません。したがって、各タスクのタイム スライスを開始し (それらを次の に実行し)、それらを無期限に切り替えるループが必要です。これは通常、スケジューラと呼ばれます。それらは非常に急速に毛むくじゃらになる傾向があるため、1 つの回答で完全なスケジューラーを記述しようとはしません。ただし、説明しようとすることができるいくつかのコアコンセプトがあります。yield
- 通常、
yield
制御をシェデュラーに戻すものとして扱います (実際にgevent.sleep(0)
は、コード内と同様です)。つまり、ジェネレーターはやりたいことを何でも実行し、コンテキスト スイッチが便利でおそらく役立つ場所にある場合は、それを実行しyield
ます。
- Python 3.3+ では、
yield from
別のジェネレーターにデリゲートするための非常に便利なツールです。使用できない場合は、スケジューラーにコール スタックをエミュレートさせ、戻り値を適切な場所にルーティングresult = yield subtasks()
し、タスクのようなことを行う必要があります。これは遅く、実装がより複雑で、有用なスタック トレースを生成する可能性は低いです (yield from
これは無料で行います)。しかし、最近まで、それは私たちが持っていた最高のものでした.
- ユースケースによっては、タスクを管理するためにさまざまなツールが必要になる場合があります。一般的な例としては、より多くのタスクを生成する、タスクが完了するのを待つ、いくつかのタスクのいずれかが完了するのを待つ、他のタスクの失敗 (キャッチされていない例外) を検出するなどがあります。これらは通常、スケジューラによって処理され、タスクには API が与えられます。スケジューラと通信します。このコミュニケーションを行うためのきちんとした (常に完璧であるとは限りませんが) 方法は、
yield
特別な価値観を伝えることです。
- ジェネレータベースのタスクと gevent (および同様のライブラリ) のかなり重要な違いの 1 つは、後者のコンテキスト スイッチは暗黙的であるのに対し、タスクではコンテキスト スイッチを簡単に識別できることです:
yield [from]
スケジューラ コードを実行できる可能性のあるもののみ。たとえば、コードが呼び出しているものを調べることなく、コードを見るだけで、コードの一部がアトミックかどうかを確認できます (他のタスクに関して。スレッドをミックスに追加する場合は、それらについて個別に心配する必要があります)。
最後に、このようなスケジューラーの作成に関するGreg Ewing のチュートリアルに興味があるかもしれません。(これは、現在の PEP 3156 についてブレインストーミングを行っているときに思いつきましたpython-ideas
。これらのメール スレッドも興味があるかもしれませんが、Web ベースのアーカイブは、半年前に書かれた数十のスレッドで数百のメールを読むのにはあまり適していません。 )