Pythonにおける同期/非同期処理

Pythonで処理を効率化する際、「同期」と「非同期」という概念を理解することが重要です。これは、タスクをどのように実行し、いつ次のタスクに進むかを決める、プログラムの実行モデルに関する考え方です。


 

同期処理(Synchronous Processing)

 

同期処理は、タスクを一つずつ順番に実行する、最も基本的な実行モデルです。あるタスクが完了するまで、次のタスクは開始しません。

 

特徴

 

  • 直列実行:タスクAが完了してから、タスクBが開始します。

  • シンプルさ:コードが上から下に書かれた順番で実行されるため、非常に分かりやすく、デバッグが容易です。

 

課題

 

同期処理はシンプルですが、ボトルネックになる場合があります。例えば、Webサイトからデータをダウンロードする際に、通信が完了するまで(I/O待ち)、プログラム全体が停止してしまいます。

 

# 同期処理の例
import time

def download_file(filename):
    print(f"'{filename}'のダウンロードを開始します。")
    time.sleep(3) # I/O待ち時間をシミュレート
    print(f"'{filename}'のダウンロードが完了しました。")

start_time = time.time()
download_file("file_A")
download_file("file_B")
end_time = time.time()

print(f"合計時間: {end_time - start_time:.2f}秒")
# 出力: 約6秒

 

この例では、ファイルAのダウンロードが完了するまで、ファイルBのダウンロードは始まりません。


 

非同期処理(Asynchronous Processing)

 

非同期処理は、タスクの完了を待たずに、次のタスクを開始する実行モデルです。あるタスクがI/O待ち状態になったら、その間に別のタスクを実行します。タスクが完了したという通知を受け取ったら、中断したタスクを再開します。

 

特徴

 

  • ノンブロッキング:I/O待ちで処理が停止することなく、効率的にCPUを使えます。

  • 単一スレッドPythonの非同期処理は、基本的に一つのスレッド内でタスクを切り替えることで実現します。そのため、CPUを多用する計算処理の高速化には向きません。

 

Pythonにおける非同期処理

 

Python 3.4以降で導入されたasyncioライブラリが、この非同期処理の中心を担います。asyncawaitといったキーワードを使って、協調的なマルチタスクを実現します。

 

# 非同期処理の例
import asyncio
import time

async def download_file_async(filename):
    print(f"'{filename}'のダウンロードを開始します。")
    await asyncio.sleep(3) # 非同期I/O待ち
    print(f"'{filename}'のダウンロードが完了しました。")

async def main():
    start_time = time.time()
    await asyncio.gather(
        download_file_async("file_A"),
        download_file_async("file_B")
    )
    end_time = time.time()
    print(f"合計時間: {end_time - start_time:.2f}秒")

if __name__ == "__main__":
    asyncio.run(main())
# 出力: 約3秒

 

この例では、ファイルAとファイルBのダウンロードが同時に開始されるため、合計時間が大幅に短縮されています。


 

まとめ:同期 vs. 非同期

 

  同期処理 (Synchronous) 非同期処理 (Asynchronous)
実行モデル 直列的(順番に実行) 並行(見かけ上、同時に実行)
得意なこと シンプルな処理、デバッグ I/O待ちが多い処理(通信、ファイル操作)
キーワード なし async, await
課題 I/O待ちでプログラム全体が停止する 学習コスト、デバッグの複雑さ

プログラムのタスクがI/O中心であれば非同期処理を、そうでなければシンプルな同期処理を基本とすると良いでしょう。