質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
並列処理

複数の計算が同時に実行される手法

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

2932閲覧

Python - asyncio.Event().waitの意義

koyamashinji

総合スコア45

並列処理

複数の計算が同時に実行される手法

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/06/19 01:45

編集2021/06/19 01:47

Pythonで非同期ネットワークIO処理実装にむけて勉強しています。
以下は、サーバから複数の__Websocket__配信を非同期的に受信するコードです(asyncio, aiohttp使用)。
(コード自体は私が一から書いたものではなく、色々なリソースを参考にしながらつなぎ合わせたものです。動作OK。)

わからないこと

asyncio.Event().waitが何のためにあるのか、どのようなケースで活用すべきなのか、いまいち理解できません

以下コード#(1), #(2)はあっても無くても挙動は変わりません。
また、ドキュメントによると、以下は理解できましたが、具体的にどのような場合に活用すべきなのかイメージが湧きません。

  • EventオブジェクトはBoolフラグで管理されている(デフォルトはFalse, set()が呼ばれたらTrue
  • wait()は、EventオブジェクトがTrueになるまで待つ

python

1import aiohttp 2import asyncio 3 4async def coro(event, 5 item1, 6 item2): 7 async with aiohttp.ClientSession() as session: 8 async with session.ws_connect(url='URL') as ws: 9 event.set() # (2) 10 await asyncio.gather(ws.send_json(item1), 11 ws.send_json(item2)) 12 async for msg in ws: 13 print(msg) 14 15async def ws_connect(item1, 16 item2): 17 event = asyncio.Event() 18 task = asyncio.create_task(coro(event, item1, item2)) 19 await event.wait() # (1) 20 return task 21 22async def main(): 23 item1 = {some json object} 24 item2 = {some json object} 25 26 ws_task = await ws_connect(item1, item2) 27 await ws_task 28 29asyncio.run(main())

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

解決済みですが、応用的な話題で


以下コード#(1), #(2)はあっても無くても挙動は変わりません。

接続が成功しなかった場合の、実際の挙動が変わります。


作用が判り難いのは、main -> ws_connect -> coro だと「直列」な呼び出しで、
~の完了後に処理したいだけなら await した後にコード書けば同じだったり、
もしくは、add_done_callback で後処理を登録したりもできます。
(違い・使い分けは、コードのスコープだったり例外時やキャンセル時の挙動)

非同期処理においては、 双方の関数のスコープを継続したまま
待ち合わせする為の同期機構として使えます。

質問のコードのケースでも、有効な使い道はあり

python

1# 接続成功後に event.set() されているので 2# 接続に成功する迄の時間のタイムアウト 3await asyncio.wait_for(event.wait(), timeout=10) 4 5# イベントによる通知がないと 6# タスク全体のタイムアウト(タイムアウト時の、接続成功の合否はわからない) 7await asyncio.wait_for(task, timeout=10)

通常の、同期コードでは、失敗時は例外を出して中断することになりますが、
asyncio等のイベント駆動では、例外の捕捉を呼び出し元からは、
対象のコルーチン(を含むタスク)を直接 await する迄できません。

python

1# asyncio での例外の扱い 2await task1 # この間(task1完了前)に task2 内部で例外が起きても 3await task2 # task2 からの例外が発生するのは、コード上ではこちら

どのようなケースで活用するかの解としては、
「コルーチン間でイベントを通知する手段」として使うことが出来ます。

同期プリミティブは、同期処理を実現するための手段の一つなので、
アイデア次第で他にも様々な用途で使うことが出来ます。

  • コルーチン内でのループの一時停止・再開

python

1async def process(event): 2 for num in range(100): 3 await event.wait() 4 print(f"{num=}") 5 6# 外部からの操作で event set()/clear() で、一時停止・再開
  • 任意のタイミングでキャンセル可能な sleep の実装

 (wait_for と組み合わせてタイムアウト処理)

python

1async def past(event, within): 2 """ 3 Examples 4 -------- 5 6 if await past(event, within=10): 7 ... # 10 秒以内に event.set() が呼ばれた。 8 else: 9 ... # 10 秒以上経過。タイムアウト時 10 """ 11 try: 12 await asyncio.wait_for(event.wait(), timeout=within) 13 except asyncio.TimeoutError: 14 pass 15 # NOTE: マルチスレッドではないので、 16 # この間に await がない限り event.set() が呼ばれることはない 17 return event.is_set() 18

参考までに、マルチプロセスやマルチスレッドでも同樣のクラス群が提供されており、
同期プリミティブは、独自の同期機構を作るための部品として扱われます。
より具体的な応用例は:「同期」や「排他制御」辺りの用語がヒント。

例えば、独自の sleep を Event を使って実装する場合、
n秒後に event.set が呼ばれるように設定された event として実装できます。

python

1async def my_sleep(delay): 2 # NOTE: event を引数で与えるようにすれば、外部からも中断可能に。 3 4 event = asyncio.Event() 5 loop = asyncio.get_running_loop() 6 7 if delay <= 0: 8 loop.call_soon(event.set) 9 else: 10 loop.call_later(delay, event.set) 11 await event.wait()

※用例を示すもので、実際の asyncio.sleep の実装では有りません。
実際の asyncio.Event, asyncio.sleep は、どちらも より低階層の API future で実装されてます。


存在意義としては、他の言語・ライブラリでも同樣の概念が確立されているので、
「車輪の再発明」を避ける為にも有用です。Pythonのライブラリ内でも

threading.Event, multiprocessing.Event に同じインターフェースを持つクラスがあり、
それぞれ スレッド間、プロセス間、で同樣の役割を果たします。

asyncio.Event は、それらに対して コルーチン間でのイベント通知・同期機構を提供します。
⇛ マルチスレッド・マルチプロセスのコードを asyncio へ移植する際に、
共通の部品が使えると便利。他の部品は、Lock や Queue 等。

投稿2021/06/20 11:36

編集2021/06/20 14:19
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koyamashinji

2021/06/22 00:46

検証しており返信が遅くなりました。いつも大変有難うございます。応用例を示して頂き大変勉強になりました。eventの主な役目は、「コルーチン間で 任意の場所でイベントを通知し合うこと(eventがない場合、コルーチン内で発生したイベントは 対象のコルーチンをawaitするまで捉えられない)」と理解しました。
guest

0

ベストアンサー

asyncio.Event().waitが何のためにあるのか.どのようなケースで活用すべきなのか

使い道としては、他のコルーチン非同期タスクを待ち受ける用途があります。

例:
worker1は作業1を、worker2は作業2を同時並行で行いますが
worker2は最終作業も行う必要があり、
なおかつ最終作業は作業1が完了していないと行えないものとします。

下記のコードでは、worker2は、worker1の作業1が完了するまで(worker1がevent.set()するまで)、event.wait()のところでブロックされ、最終作業を開始できません。

python

1import asyncio 2import random 3 4async def worker1(event): 5 interval = random.randint(1,8) 6 print(f"worker1: 作業1を開始します。{interval}秒かかります...") 7 await asyncio.sleep(interval) 8 print(f"worker1: 作業1 が完了しました。worker2に完了を通知します。」") 9 event.set() 10 11async def worker2(event): 12 interval = random.randint(1,8) 13 print(f"worker2: 作業2を開始します。{interval}秒かかります...") 14 await asyncio.sleep(interval) 15 print(f"worker2: 作業2が完了しました。") 16 if not event.is_set(): 17 print(f"worker2: 作業1 が完了するのを待っています...") 18 await event.wait() 19 print(f"worker2: 作業1の完了通知を受領しました。") 20 print(f"worker2: 最終作業を開始します。2秒かかります...") 21 await asyncio.sleep(2) 22 print(f"最終作業が完了しました。") 23 24 25async def main(): 26 event = asyncio.Event() 27 _worker1 = asyncio.create_task(worker1(event)) 28 _worker2 = asyncio.create_task(worker2(event)) 29 await asyncio.gather(_worker1,_worker2) 30 31 32asyncio.run(main())

投稿2021/06/19 04:31

編集2021/06/19 04:41
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koyamashinji

2021/06/19 13:14

いつもありがとうございます。例が非常に分かりやすく理解できました。取り急ぎお礼申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問