はじめに
業務でtime.sleep()
を使った待機処理を学ぶ中で、非同期処理への興味から個人的にasyncio.sleep()
についても学びました。
time.sleep()
は同期的にプログラムを停止する一方で、asyncio.sleep()
は非同期プログラムに適しており、待機中も他の処理が並行して進行します。
この記事では、非同期処理におけるasyncio.sleep()
の使い方と、同期的なtime.sleep()
が引き起こす問題を備忘録として整理します。
1. time.sleep()
とasyncio.sleep()
の違い
1-1. time.sleep()
の基本
-
time.sleep()
はプログラム全体の実行を停止し、指定した秒数だけ待機します。 - シンプルな待機処理では便利ですが、非同期プログラムには不向きです。
import time
print("処理開始")
time.sleep(2) # 2秒待機
print("2秒後に処理再開")
実行結果:
処理開始
2秒後に処理再開
解説:
1.print("処理開始")
- プログラムが開始され、「処理開始」が表示される。
2.time.sleep(2)
- 2秒間プログラム全体が停止する。この間、他の操作は行われない。
3.print("2秒後に処理再開")
- 2秒が経過した後、次の行が実行され、「2秒後に処理再開」が表示される。
1-2. asyncio.sleep()
の基本
- 非同期処理では、複数のタスクを同時並行で実行できます。
- Pythonでは、非同期の待機に
asyncio.sleep()
を使います。
これにより、1つのタスクが待機中でも、他のタスクが進行できます。
(※)asyncio:
Pythonで非同期処理を扱うための標準ライブラリ。
イベントループを使って、複数のタスクを同時並行で処理することができる。
2. asyncio.sleep()
の例
- 以下は、
asyncio.sleep()
を使った非同期プログラムの例です。 - 複数のタスクを並行して実行し、それぞれの待機時間後に結果を出力します。
import asyncio
async def task(name, delay):
print(f"{name} 開始")
await asyncio.sleep(delay) # 非同期で待機
print(f"{name} 完了: {delay}秒後")
async def main():
await asyncio.gather(
task("タスク1", 2),
task("タスク2", 3),
)
asyncio.run(main())
実行結果:
タスク1 開始
タスク2 開始
タスク1 完了: 2秒後
タスク2 完了: 3秒後
解説: 実行順序のポイント
-
main()
が開始され、asyncio.gather()
でタスク1とタスク2が同時に実行される。- タスク1は2秒待機、タスク2は3秒待機する。
- タスク1とタスク2は同時に「開始」メッセージを出力する。
- 2秒後、タスク1が完了し、完了メッセージが表示される。
- さらに1秒後、タスク2が完了し、全てのタスクが終了する。
3. ブロッキングの問題:time.sleep()
の危険性
- 次に、非同期プログラム内で誤って
time.sleep()
を使った場合、全体の処理がどのようにブロックされるかを確認します。
import asyncio
import time
async def blocked_task():
print("NG例: time.sleep()を使うと…")
time.sleep(2) # 他の処理が2秒間ブロックされる
print("この間に他の処理が実行されない")
async def normal_task(name):
for i in range(5):
print(f"{name} 実行中: {i+1}/5")
await asyncio.sleep(1) # 非同期で1秒ごとに実行
async def main():
await asyncio.gather(
blocked_task(), # ブロックされるタスク
normal_task("タスクA"), # 並行で実行されるべきタスク
)
asyncio.run(main())
実行結果:
NG例: time.sleep()を使うと…
この間に他の処理が実行されない
タスクA 実行中: 1/5
タスクA 実行中: 2/5
タスクA 実行中: 3/5
タスクA 実行中: 4/5
タスクA 実行中: 5/5
解説: なぜブロッキングが起こるのか?
-
time.sleep()
は同期的な待機を行うため、非同期環境でも全体の進行を止めてしまいます。 - この例では、time.sleep(2)の間、他の非同期タスク(normal_task)が進まなくなり、全体がブロックされてしまいます。
4. 解決策:非同期処理ではasyncio.sleep()
を使おう
- ブロッキングを避けるために、非同期プログラムでは
asyncio.sleep()
を使います。 - 修正したコードは以下の通りです。
import asyncio
async def blocked_task():
print("非ブロッキングの例")
await asyncio.sleep(2) # 非同期待機
print("他の処理も並行して実行される")
async def normal_task(name):
for i in range(5):
print(f"{name} 実行中: {i+1}/5")
await asyncio.sleep(1) # 非同期で1秒ごとに実行
async def main():
await asyncio.gather(
blocked_task(), # 非ブロッキングのタスク
normal_task("タスクA"), # 並行で実行されるタスク
)
asyncio.run(main())
実行結果:
非ブロッキングの例
タスクA 実行中: 1/5
タスクA 実行中: 2/5
他の処理も並行して実行される
タスクA 実行中: 3/5
タスクA 実行中: 4/5
タスクA 実行中: 5/5
5. まとめ
- time.sleep()は同期処理で使い、非同期処理では使わない。
- 非同期環境ではasyncio.sleep()を使うことで、他のタスクがブロックされない。