Pythonのasyncによる非同期処理の仕組みと使い方をわかりやすく解説

スポンサーリンク
スポンサーリンク
amazon
スマイルSALE
--:--:--
ad. 価格範囲を指定して商品を探せます

asyncの概要

非同期処理を定義する Python予約語

async

概要 async(エイシンク)は、Pythonにおいて「非同期処理(並行処理)」を実装するための最重要キーワードです。async def の形式で宣言することで、呼び出してもすぐには実行されず、途中で処理を一時停止・再開できる特殊な関数(コルーチンオブジェクト)を定義することができます。

わかりやすく説明 Web上のデータダウンロードやデータベースの読み書きなど、「プログラム側は何もせず、相手の応答をじっと待つだけの無駄な時間(I/O待ち)」が発生するときに、その隙間時間を使って別の仕事を裏で進めておくための仕組みです。1つのタスクが立ち往生している間に他のタスクにCPUの主導権をサッと譲ることで、全体の処理時間を劇的に短縮させることができます。

  • async def で定義された関数は「コルーチン」と呼ばれ、非同期処理の独立した1つのタスクとして扱われます。
  • 非同期関数の内部では、相棒となる await キーワードをセットで使用し、「ここで待ち時間が発生するから、一度主導権を他人に譲るよ」とシステムに伝達します。
  • Python標準の asyncio(エイシンク・アイオー)ライブラリと協調し、イベントループと呼ばれる管制塔を通じて処理をコントロールするのが基本ルールです。

asyncの考え方をイメージで理解

asyncの基本的な使い方

async def で非同期関数を形作り、それを実行する起点として標準ライブラリの asyncio.run() を組み合わせて起動する基本形です。

import asyncio

# async def で非同期関数(コルーチン)を定義
async def say_hello():
    print("処理を開始します。")
    # 2秒間の待機(この待ち時間中に、システムは他の非同期処理を裏で進められます)
    await asyncio.sleep(2)  
    print("2秒経ちました。お待たせしました!")

# 【最重要】非同期の世界を起動するための専用のエントリポイント
asyncio.run(say_hello())
  • 非同期関数の中の「待機処理」には、通常の time.sleep() ではなく、非同期に対応した await asyncio.sleep() を使用するのが絶対的なルールです。

asyncとawaitの組み合わせ(複数タスクの並行実行)

複数の独立した非同期タスクを生成し、asyncio.gather() を使ってすべての処理を同時に「よーいドン」で一斉スタートさせる実務の手法です。

import asyncio
import time

async def task_cooking():
    print("[料理] お湯を沸かし始めました(3秒待ち)")
    await asyncio.sleep(3)  # お湯が沸くまで別の仕事をしててOK
    print("[料理] お湯が沸いたので麺を投入して完成!")

async def task_laundry():
    print("[洗濯] 洗濯機のスイッチを入れました(2秒待ち)")
    await asyncio.sleep(2)  # 洗濯が終わるまで別の仕事をしててOK
    print("[洗濯] 洗濯が完了しました!")

async def main():
    # 2つのタスクを同時に並行して走らせる
    await asyncio.gather(task_cooking(), task_laundry())

start_time = time.time()
asyncio.run(main())
print(f"全体の実行時間: {time.time() - start_time:.1f} 秒") 
# 結果: 約5秒ではなく、一番長いタスクに合わさるため「約3.0秒」で終了します!
  • 料理の3秒の待ち時間を利用して、裏で洗濯の2秒を同時に消化するため、バラバラに実行するよりも圧倒的な速度で完了します。

asyncの実践的な使用例(Web通信の一斉処理)

実務の現場で最も async が真価を発揮するのが、複数の外部サーバーから同時にデータを一斉にダウンロードしてくるスクレイピングやAPI連携の処理です。

import asyncio
import httpx  # 非同期HTTP通信用のモダンなライブラリ(要インストール)

async def fetch_data(api_id):
    # 非同期対応のコンテキストマネージャには async with を使用する
    async with httpx.AsyncClient() as client:
        url = f"https://jsonplaceholder.typicode.com/posts/{api_id}"
        response = await client.get(url)
        return response.json()

async def main():
    # 1から5までのAPIリクエストタスクをリストにまとめる
    tasks = [fetch_data(i) for i in range(1, 6)]
    
    # 5つのAPIへ同時に一斉アクセスを開始し、結果をまとめて回収
    results = await asyncio.gather(*tasks)
    
    for data in results:
        print(f"取得したデータのタイトル: {data.get('title')[:20]}...")

asyncio.run(main())
  • 1リクエストごとに相手の応答をじっと待つ「同期処理」とは違い、5つのリクエストをほぼ同時に投げて一括回収するため、通信遅延によるロスを極限まで排除できます。

asyncの注意点

  • 普通に関数呼び出しをするだけでは中身が1行も動かない(初心者が絶対にハマる罠)async def で作った関数は、通常の関数のようにつなげて say_hello() と記述するだけでは、内部のプログラムは一切動きません。実行されずに「コルーチンオブジェクト」という予約券だけが発行されて素通りしてしまいます。起動するには必ず頭に await を付与するか、最上流で asyncio.run() に包む必要があります。
    # 【何も実行されない悪い失敗コード】
    async def download():
        print("ダウンロード開始")
    
    download() # ❌これだけでは何も表示されず、動作もしません(警告が出ます)
    
    # 【正しく起動する対策コード】
    asyncio.run(download()) # ⭕管制塔を通じて正式にコルーチンを発動させる
  • 非同期ブロックの内部に「普通の居眠り(time.sleep)」を絶対に混ぜてはいけない(実務での大惨事バグ)async def の処理空間の中で、非同期非対応の time.sleep() や、非同期に対応していない重い通信処理(requests ライブラリなど)を1箇所でも混ぜてしまうと、その瞬間にPythonのシングルスレッドの頭脳が完全にロック(ブロッキング)されます。これにより、並行して動いていた他のすべてのタスクまで道連れで完全フリーズします。必ず await asyncio.sleep() や非同期専用ライブラリに徹底統一してください。
    # 【他のタスクを全員フリーズさせる最悪の失敗コード】
    async def worker_bad():
        # これが入った瞬間、イベントループ全体の時間がストップし、非同期の意味が完全消滅します
        time.sleep(5) 
        print("復帰しました")
    
    # 【全体に迷惑をかけない正しい対策コード】
    async def worker_good():
        # 5秒待つ間、主導権を一時的に他のタスクに譲るため安全です
        await asyncio.sleep(5) 
        print("復帰しました")

asyncのよくある質問

Q: `async` を導入しさえすれば、どんな重いプログラムでも処理速度が爆速になりますか?
A: いいえ、処理の性質によっては全く速くならないどころか、かえって遅くなることもあります。 async が劇的な効果を発揮するのは、ネットワーク通信やディスクの読み書きといった「CPUが手を止めて、外部の応答を待つだけの時間(I/Oバウンドな処理)」が中心のプログラムだけです。 一方で、高画質な画像の加工、大量の行列計算、機械学習のトレーニングといった「CPUの演算能力をフルパワーで消費し続ける処理(CPUバウンドな処理)」の場合、隙間時間(待ち時間)そのものが存在しないため、async を使っても速度は一切向上しません。CPUバウンドな処理を高速化したい場合は、async ではなく multiprocessing(マルチプロセス)による物理的な「並列処理」を選択するのが正解です。
Q: 「並行処理(async)」と、一般的な「並列処理(マルチスレッド/マルチプロセス)」の構造的な違いを分かりやすく教えてください。
A: 「超高速のワンオペ皿回し(並行)」か、「物理的にスタッフを増やす(並列)」かの違いです。 「並列処理」は、複数のCPUコア(複数のスタッフ)を同時に動かして、文字通り2つの異なる計算を同時に同時進行で解くスタイルです。 これに対し、async が提供する「並行処理」は、CPUのコア(スタッフ)はあくまで1人のワンオペ状態です。ただし、このスタッフは神業的なスピードを持っており、「タスクAが通信の返事待ちに入った瞬間に、1マイクロ秒も無駄にせずタスクBの作業に手をつけ、タスクBが待ちに入ったらタスクCへ行く」という風に、待ち時間を利用して複数の皿を1人で超高速で回し変えているイメージです。そのため、1つの頭脳であっても無駄なタイムロスを徹底的にゼロにできるメリットがあります。

まとめ

async は、コンピュータの頭脳に一切のサボり時間を許さず、待ち時間を100%有効活用して複数のタスクを限界まで詰め込むための「時間効率化の超強力なキーワード」です。

  • async def を用いていつでも一時停止・再開ができる「コルーチン」を生成し、非同期の世界を定義する。
  • 非同期ブロック内では await と非同期専用ライブラリ(httpxaiohttp など)を揃えることで、ブロッキングフリーズを徹底回避する。
  • API通信、Webスクレイピング、非同期Webサーバー(FastAPIなど)といった、大量のI/O待ちが頻出する実務の最前線における必須技術。

「待っている間に、別の有意義な仕事を終わらせる」という非同期ハンドリングの感覚をマスターし、ユーザーを1秒も無駄に待たせない圧倒的スピードのモダンなアプリケーションを構築できるようになりましょう。