私のコードがasyncio.get_event_loop().run_until_complete(foo())
を2回呼び出すため、このエラーが発生していると思います。 1回はfoo()
から、2回目はfoo()
によって呼び出された関数から。私の質問は次のとおりです。なぜこれが問題になるのでしょうか?このループが実行されていることに気をつけなければならないのはなぜですか?
この質問に対して編集が行われ、それがあいまいになったと思います(ルールを理解せずにルールに従うことを好む人もいるため、タイトルから「不正な」単語が削除されました)。残念ながら、これにより混乱が生じます。
エラーが発生するという事実には驚かない。 asyncio
のソースまでさかのぼると、このライブラリの作成者がこの方法でそれを実行したかったことがわかります。謎はありません。不可解な部分は、ライブラリの作成者が、イベントループから、ループが既に実行されているときに何らかの機能を実行するように要求することは違法であると判断した理由です。
問題をこのような2つの呼び出しに減らすことができます。ケース分析により、これらが3つの可能性であることがわかります。
さて、3つのケースすべてに対処する正気な振る舞いはありますか?私には、ここで複数の正気な振る舞いが可能であるか、おそらく可能であることは明らかです。例えば:
run_until_complete()
の最初のインスタンスに続くコードに制御を返しません(したがって、run_until_complete()
の後にコードは実行されません)。run_until_complete
を呼び出した最初のコードオブジェクトに制御を返します。今、私はこの振る舞いが誰もが望むものではないかもしれないことを理解することができます。しかし、このライブラリはプログラマーにイベントループの開始/停止を制御することを決定したため、このような決定の結果も満たす必要があります。同じループを複数回開始することをエラーにすると、ライブラリコードがこれを実行できなくなり、asyncio
を使用するライブラリの品質と有用性が低下します(たとえば、aiohttp
の場合) 。
イベントループ実行-非同期プログラムのエントリポイントです。すべてのコルーチン、タスク、コールバックの実行を管理します。実行中にループを実行することは意味がありません。ある種では、既に実行中の同じジョブエグゼキューターからジョブエグゼキューターを実行しようとするようなものです。
この質問があるので、asyncioの仕組みを誤解しているのではないかと思います。 この記事 を読んでください-大きくはなく、良い紹介です。
更新:
このループが既に実行されている間に、イベントループによって実行される複数のものを追加してもまったく問題はありません。それを待つだけでそれを行うことができます:
await coro() # add coro() to be run by event loop blocking flow here until coro() is finished
またはタスクの作成:
asyncio.ensure_future(coro()) # add coro() to be run by event loop without blocking flow here
ご覧のとおり、イベントループのメソッドを呼び出して実行する必要はありません。
run_forever
やrun_until_complete
などのイベントループのメソッドは、単にイベントループを一般的に開始するためのものです。
run_until_complete(foo())
は、「イベントループによって実行されるfoo()
を追加し、foo()
が実行されなくなるまでイベントループ自体を実行する」ことを意味します。
Nest_asyncを使用して問題を解決しました
pip install nest_asyncio
ファイルに以下の行を追加します。
import nest_asyncio
nest_asyncio.apply()
ひいきにするのではなく、これを書き留めていますが、イベントループの実行中に非同期関数を単純にキューに入れ、結果を同期的に待機する状況を処理する方法を説明するために機能しません。
run_until_complete
は、任意の数の非同期関数を同期的に実行するためのものではなく、非同期プログラム全体のメインエントリポイントを実行するためのものです。この制約は、ドキュメントからすぐにはわかりません。
Aiohttpなどのライブラリはサーバーとして実行する独自のエントリポイントをキューに入れ、run_until_complete
またはrun_forever
を使用してループの同期操作をブロックするため、イベントループは既に実行されており、実行できません。そのイベントループに対する独立した同期操作と、そのスレッド内での結果の待機。
そうは言っても、同期コンテキスト内から非同期操作を実行中のイベントループにキューイングし、通常の関数のように結果を取得する必要がある場合、それは不可能な場合があります。最善の策は、非同期操作が終了したときに呼び出される同期コールバックを渡すことです。もちろん、イベントループが遅くなります。
状況を処理する別の方法は、使用している非同期httpライブラリの起動コールバックとクリーンアップコールバック内でコードを実行することです。これが sample の方法です。