免責事項:これはを試して私の最初の時間であるasyncio
のモジュール。
私が使用しているasyncio.wait
非同期タスクのセットからすべての結果を待っているタイムアウト機能をサポートしようと、次のように。これは、より大きなライブラリーの一部であるので、私はいくつかの無関係なコードを省略しています。
ライブラリがすでに私は本当に私がこれをやっている理由について、それらの代わりにや質問を使用する提案には興味がないので、タスクを提出し、ThreadPoolExecutorsとProcessPoolExecutorsでタイムアウトを使用してサポートすることに注意してくださいasyncio
。コードの上...
import asyncio
from contextlib import suppress
...
class AsyncIOSubmit(Node):
def get_results(self, futures, timeout=None):
loop = asyncio.get_event_loop()
finished, unfinished = loop.run_until_complete(
asyncio.wait(futures, timeout=timeout)
)
if timeout and unfinished:
# Code options in question would go here...see below.
raise asyncio.TimeoutError
最初のIでタイムアウトに保留中のタスクをキャンセルについての心配はありませんでしたが、その後、私は警告だTask was destroyed but it is pending!
プログラム終了または上をloop.close
。ビットを研究した後、私は仕事をキャンセルして、実際にキャンセルするためにそれらを待つために複数の方法を見つけました:
オプション1:
[task.cancel() for task in unfinished]
for task in unfinished:
with suppress(asyncio.CancelledError):
loop.run_until_complete(task)
オプション2:
[task.cancel() for task in unfinished]
loop.run_until_complete(asyncio.wait(unfinished))
オプション3:
# Not really an option for me, since I'm not in an `async` method
# and don't want to make get_results an async method.
[task.cancel() for task in unfinished]
for task in unfinished:
await task
オプション4:
以下のようにwhileループのいくつかの並べ替え、この答え。私の他のオプションは優れているが、完全を期すために含むように思えます。
オプション1と2は両方とも、これまでの仕事の罰金に思えます。オプションは、「右」かもしれませんが、どちらかとasyncio
長年にわたって進化するネットの周りの例や提案は、いずれかの古いまたはかなり変化しています。だから、私の質問があります...
質問1
オプション1と2の間の任意の実用的な違いはありますか?私が知っているrun_until_complete
オプション1は、私が以前にタスクが実際に完了に時間がかかる場合、それは異なった動作ができたとし、特定の順番でループしているので、以来、未来が完了するまで実行されます。私はあれば理解することがasyncioソースコードを見てみましたasyncio.wait
だけで効果的にボンネットの下にそのタスク/先物と同じことを行いますが、それは明らかではなかったです。
質問2
課題の一つは、それが実際にはすぐにキャンセルしないことがあり長時間実行ブロック操作の途中である場合、私は仮定しますか?おそらく、基本となる操作やライブラリが使用されている場合だけに依存していることをすぐにかCancelledErrorを上げるのだろうか?たぶんそれはasyncioのために設計されたライブラリで発生することはありませんか?
私はここでタイムアウト機能を実装しようとしているので、私はこれにやや敏感です。それが可能だ場合は、これらのものは、私が呼び出して検討したいキャンセルするのに長い時間かかることがありcancel
、それが実際に起こるのを待っていない、または仕上げにキャンセルが待つために、非常に短いタイムアウトを設定します。
質問3
それが可能であるloop.run_until_complete
(または、本当に、と下位の呼び出しasync.wait
に)戻り値unfinished
タイムアウト以外の理由のために?もしそうなら、私は明らかに私のロジックビットを調整する必要があると思いますが、からドキュメントそれが不可能であるように思えます。
オプション1と2の間の任意の実用的な違いはありますか?
第オプション2つのルックスよりよいとわずかに、より効率的かもしれないが、彼らの正味の効果は同じです。
私が知っている
run_until_complete
オプション1は、私が以前にタスクが実際に完了に時間がかかる場合、それは異なった動作ができたとし、特定の順番でループしているので、以来、未来が完了するまで実行されます。
これは、最初はそのように見えますが、ので、それは実際にはそうではないのですloop.run_until_complete
が実行すべてのループに提出したタスクは、1つだけでなく、引数として渡されました。それは単に停止してawaitable完了後- 「完全なまで実行」に言及するものです。呼び出しループrun_until_complete
すでにスケジュールされたタスクの上には、以下の非同期コードのようなものです:
ts = [asyncio.create_task(asyncio.sleep(i)) for i in range(1, 11)]
# takes 10s, not 55s
for t in ts:
await t
これは、順番に次のスレッドコードと意味的に等価です。
ts = []
for i in range(1, 11):
t = threading.Thread(target=time.sleep, args=(i,))
t.start()
ts.append(t)
# takes 10s, not 55s
for t in ts:
t.join()
つまり、await t
およびrun_until_complete(t)
ブロックまでt
完了しているが、他のすべてが可能-など使用して、以前にスケジュールされたタスクとしてasyncio.create_task()
だけでなく、その時に実行します。だから、総実行時間が最も長いタスクのではなく、それらの合計の実行時間を等しくなります。最初のタスクは、長い時間を取るために起こった場合、他のすべては、その間に仕上がっています、そして彼らの待ち受けは、すべての睡眠ではないでしょう。
このすべては、以前にスケジュールされたタスクを待っているに適用されます。あなたはコルーチンにそれを適用しようとすると、それは動作しません。
# runs for 55s, as expected
for i in range(1, 11):
await asyncio.sleep(i)
# also 55s
for i in range(1, 11):
t = threading.Thread(target=time.sleep, args=(i,))
t.start()
t.join()
これは、多くの場合、その最後のasyncio例にコードと同等を書き、それを並列に実行することを期待asyncio初心者のためのこだわりのポイントです。
私はあれば理解することがasyncioソースコードを見てみました
asyncio.wait
だけで効果的にボンネットの下にそのタスク/先物と同じことを行いますが、それは明らかではなかったです。
asyncio.wait
2つのことを行い、単に便利なAPIは以下のとおりです。
- 入力引数は実装されていることを何かに変換します
Future
。コルーチンであるかのように、それは、イベントループにそれらを提出することを意味しているためcreate_task
、それらが独立して実行することを可能にします、。あなたはそれをあなたがそうであるように、そもそもタスクを与えた場合は、このステップはスキップされます。 - 用途は
add_done_callback
先物が行われたときに、その発信者を再開した時点で、通知します。
そうです、それは同じことを行いますが、異なる実装でそれはより多くの機能をサポートしているため。
課題の一つは、それが実際にはすぐにキャンセルしないことがあり長時間実行ブロック操作の途中である場合、私は仮定しますか?
そこasyncioでの操作、一時停止、彼らはすぐにキャンセルされなければならないもののみを「ブロッキング」するべきではありません。この例外はしてasyncio上に仮付けたコードブロックしているrun_in_executor
基本的な操作は一切キャンセルしないだろう、しかし、asyncioコルーチンはすぐに例外が発生します。
おそらく、基本となる操作やライブラリが使用されている場合だけに依存していることをすぐにかCancelledErrorを上げるのだろうか?
ライブラリはありません上げる CancelledError
、それが受け取り、キャンセルが発生する前に一時停止することが起こったのawait時点でそれを。ライブラリのためにキャンセルの効果がされawait ...
、その待機を中断し、すぐに引き上げCancelledError
。キャッチされない限り、例外は機能とを介して伝播されますawait
すべての道その調達トップレベルのコルーチンに呼び出しCancelledError
マークタスク全体をキャンセルとして。asyncioコードは、おそらく使用して、ということだけで行います行儀finally
、彼らが保持するOSレベルのリソースを解放します。ときにCancelledError
キャッチされ、コードがない場合はキャンセルを効果的に無視されているリレイズそれに選択することができます。
それは(または実際に、基本となる呼び出すことが可能であるloop.run_until_complete
async.wait
タイムアウト以外の理由で未完成で戻り値)?
あなたが使用している場合return_when=asyncio.ALL_COMPLETE
(デフォルト)、それは可能ではありません。これは、とかなり可能であるreturn_when=FIRST_COMPLETED
、それは独立して、タイムアウトの明らかに可能です。