私は次のように、ミックス同期および非同期の仕事というライブラリを書きたいです。
def do_things():
# 1) do sync things
# 2) launch a bunch of slow async tasks and block until they are all complete or an exception is thrown
# 3) more sync work
# ...
私が使ってこれを実装し始めasyncio
ライブラリを学ぶ学ぶための口実として、私はより多くを学び、これは間違ったアプローチかもしれように思えます。私の問題は、行うにはきれいな方法があるようには思えないということです2
、それは、呼び出し元のコンテキストに依存するため、。例えば:
- 私が使用することはできません
asyncio.run()
、呼び出し元がすでに実行中のイベントループを持っている可能性があるため、あなたが唯一のスレッドごとに一つのループを持つことができます。 - マーキング
do_things
としてはasync
、それはいけませんので、重すぎる必要は非同期であることを発信者。プラス、あればdo_things
してasync
、(同期コードを呼び出す1
&3
から)async
関数ではあると思われる悪い習慣。 asyncio.get_event_loop()
それは左のランニングが呼び出した後に、独自のループを作成するから、発信者を妨げる場合は、新たなループ、作成する可能性があるため、また、間違っているようだdo_things
(恐らく間違いなく、彼らはそれを行うべきではないけれども)を。そして、の文書に基づいてloop.close
、それは単一のスレッドで複数のループが動作しません起動/停止のように見えます。
基本的には私が使用したい場合のように思えるasyncio
のすべてで、私はプログラムの全体の寿命のためにそれを使用するように強制していますので、このようなすべてのライブラリは、どちらかが100%同期または100%非同期のように記述する必要があります。しかし、私が欲しいの動作は次のとおりです。使用現在のイベントループ1が、そうでない場合だけの範囲のための一時的なものを作成し、実行している場合2
、そうすることで、クライアントコードを壊しません。このようなものが存在しないか、あるasyncio
間違った選択?
呼び出し側がすでに実行中のイベントループを持っている可能性があるため、私は、asyncio.run()を使用することはできませんし、あなただけのスレッドごとに一つのループを持つことができます。
呼び出し側は、実行中のイベントループを持っている場合は、呼び出し側のループをブロックしますので、あなたが最初の場所でコードをブロックする実行しないでください!
このことを念頭に置いて、あなたの最良のオプションは、実際に作ることでdo_things
使用して非同期およびコール同期符号run_in_executor
そのユースケースのために精密に設計されています。
async def do_things():
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, sync_stuff)
await async_func()
await loop.run_in_executor(None, more_sync_stuff)
このバージョンのdo_things
ように非同期コードから使用できるawait do_things()
ようにシンクコードからasyncio.run(do_things())
。
あなたが同期コードは非常に簡単に実行されることを知っている、またはあなたは、呼び出し元のイベントループを遮断するために喜んでいくつかの理由である場合、あなたは別のスレッドでイベントループを開始することにより、制限を回避することができますことを...言いました:
def run_async(aw):
result = None
async def run_and_store_result():
nonlocal result
result = await aw
t = threading.Thread(target=asyncio.run, args=(run_and_store_result(),))
t.start()
t.join()
return result
do_things
その後、次のようになります。
async def do_things():
sync_stuff()
run_async(async_func())
more_sync_stuff()
これは、両方の同期および非同期コードから呼び出し可能になりますが、コストはそれを次のようになります。
- それは、各ブランドの新しいイベントループと毎回作成されます。(あなたは、イベントループをキャッシュし、それを終了することはできませんけど。)
- 非同期コードから呼び出された場合、それはほとんどの時間は、実際に独自の非同期コード内で費やされた場合でも、効果的にそのasyncio使用を壊し、呼び出し側のイベントループをブロックします。