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ライブラリが、この非同期処理の中心を担います。async、awaitといったキーワードを使って、協調的なマルチタスクを実現します。
# 非同期処理の例
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. 非同期
プログラムのタスクがI/O中心であれば非同期処理を、そうでなければシンプルな同期処理を基本とすると良いでしょう。