2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】Sleep処理の活用とブロッキングの落とし穴

Posted at

はじめに

業務で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秒後

解説: 実行順序のポイント

  1. main()が開始され、asyncio.gather()でタスク1とタスク2が同時に実行される。
    • タスク1は2秒待機、タスク2は3秒待機する。
  2. タスク1とタスク2は同時に「開始」メッセージを出力する。
  3. 2秒後、タスク1が完了し、完了メッセージが表示される。
  4. さらに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()を使うことで、他のタスクがブロックされない。
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?