当サイトは、アフィリエイト広告を利用しています

【FastAPI】非同期と並行処理の構造を理解する

作成日:2026月02月23日
更新日:2026年02月23日

PythonのFastAPIを使ってREST APIを実装していると、次のようなことが疑問を持った。

  • 「非同期」と「並行処理」の違いってなに?
  • FastAPIはどこで並行しているのか?
  • asyncを付けたのに速くならない
  • 同時リクエストが増えると急に遅くなる

FastAPIは「非同期対応フレームワーク」と言われるが
しかし実際にどの単位で、どのレイヤーで並行処理が行われているのかを構造的に
理解していると気づいたので、調べてみた。

本記事では、FastAPIの非同期、並行処理の構造を以下の順で整理する。

  • 非同期処理とは何か
  • 並行処理とは何か
  • FastAPIはどこで並行しているのか
  • 誤解しやすいポイント

非同期処理とは何か?

非同期処理とは、

-待ち時間で処理をブロックしない設計

のこと。

I/O処理の完了を待つ間にスレッドを占有しないようにすること。

ここで重要なのは、

  • 非同期 = 並行そのものではない
  • 並行処理を可能にする仕組みである

つまり、整理すると

  • 非同期 → 実行の分離
  • 並行 → 実行の進行

になる。

並行処理を実現するためには、処理を一時停止し、制御を他へ渡せる仕組みが必要になる。
その役割を担うのが非同期処理である。

並行処理とは何か

並行処理(Concurrency)とは

  • 複数の処理が同時に進んでいるように見える状態

だが、実際には1つのスレッドが処理を切り替えながら進めている
並列(Parallelism)とは異なるので注意が必要。

整理すると

  • 並列処理 → 処理AとBを物理的に同時実行する(複数CPU・複数コア)
  • 並行処理 → 処理AとBを切り替えながら進める(時間を分割)

になる。図にすると下記のようなイメージ

処理図
並列:
AAAAA
BBBBB
並行:
AABBABABBA

FastAPIのasyncは並列ではなく、並行処理の構造になる

FastAPIはどこで並行しているのか?

前提の基本的な概念として

  • 非同期処理
  • 並行処理

がある。

ではここからはFastAPIにおいては、どんな構造で並行処理を実現させているのかを
整理する

FastAPIは単独で並行処理を実装しているわけではない。
並行処理を成立させているのは

  • ASGIという仕様
  • ASGIサーバ
  • asyncioイベントループ
  • コルーチン

である。

FastAPIはこれらの上に乗って動作するアプリケーションである。

asyncio・イベントループ・タスク(コルーチン)の関係

FastAPIは Pythonのasyncio上に構築されたWebフレームワークなので
asyncioやコルーチンついて先に整理する。

asyncioとは?

asyncioは

  • Python標準の非同期I/Oフレームワーク

  • イベントループでタスク管理

を行う

FastAPIのasyncは実際にはasyncio上で動作している。

コルーチンとは?

async def で定義されたものはコルーチン関数となる

コルーチン
async def read():
return 1

呼び出すと

呼び出し
coro = read()
print(type(coro))
# <class 'coroutine'>

返るのはまだ実行されていないオブジェクト。

これは「実行途中で停止可能な処理のインスタンス」であり、
まだ処理は開始されていない。

実行するには

実行
result = await coro
print(result)
# 1

await を付けることで

  • コルーチンの実行が開始される
  • 実行途中で awaitに到達すると一時停止する
  • 制御がイベントループへ戻る
  • 完了後に再開される

されるようになる。

asyncioの概念図

bash
asyncio
└── Event Loop
├── Task(Coroutine A)
├── Task(Coroutine B)
└── Task(Coroutine C)
  • asyncio
    • 非同期処理のフレームワーク全体
  • Event Loop(イベントループ)
    • タスクを管理・実行するスケジューラ
  • Task(タスク)
    • Coroutineを実行するためのラッパー
  • Coroutine(コルーチン)
    • await可能な処理オブジェクト(async defで定義)

イベントループが直接管理するのはTaskであり、Taskの中でCoroutineが実行される。

実行イメージ

bash
┌─────────────────────┐
│ asyncio │
│ │
│ ┌─────────────┐ │
│ │ Event Loop │ │
│ │ │ │
│ │ A → await │ │
│ │ B → 実行 │ │
│ │ C → 待機 │ │
│ └─────────────┘ │
└─────────────────────┘

動作の流れ

  • コルーチンA実行
  • Aが await
  • イベントループがBへ切り替え
  • AのI/O完了
  • Aを再開

関係を整理

関係を整理すると

  • asyncio → 非同期実行基盤
  • イベントループ → スケジューラ
  • タスク(コルーチン) → 実行単位

タスクをイベントループが管理し、その全体をasyncioが提供している。

asyncioについては下記記事でも解説してます

ASGIとFastAPIのレイヤー構造

全体構造はこうなる。

全体構造
クライアント
Uvicorn(ASGIサーバ)
asyncioイベントループ
FastAPI(ASGIアプリ)
エンドポイント(コルーチン)
  • FastAPIはUvicorn(ASGIサーバ)のasyncio上で動作する。

関係する技術としては

  • FastAPI
  • ASGI
  • Uvicorn
  • asyncio

がある

ASGIとは?

ASGIとは:

  • Pythonにおける非同期Webアプリケーションの標準インターフェース仕様

サーバとアプリの通信契約を定義している。

Uvicornとは?

Uvicornは

  • ASGI仕様を実装したWebサーバ

で役割としては

  • HTTP接続を受け取る
  • ASGIアプリ(FastAPI)を呼び出す
  • イベントループを起動する

がある

FastAPIアプリをUvicorn上で起動した場合、Uvicornから呼び出される。

ASGIサーバがやっていること

Uvicornは

  • イベントループを起動する
  • リクエストを受け取る
  • アプリをコルーチンとして呼ぶ

をしている。

実装例でいうと

app/asgi.py
from app.main import create_app
app = create_app()

FastApiアプリケーションをASGIサーバーで起動するための
エントリーポイントとなるモジュール

ASGIサーバーは起動時、ここにFastApiアプリケーションインスタンスを探しにくる

app/main.py
from fastapi import FastAPI
import app.endpoints as endpoints
def create_app():
# アプリケーションインスタンスの作成
app = FastAPI()
# ルーティング設定
app.include_router(endpoints.router)
return app
  • FastApiアプリケーションインスタンスの作成

リクエスト単位の並行処理

FastAPIは Pythonのasyncio上に構築されたWebフレームワークなので

  • リクエストごとにエンドポイントのコルーチンオブジェクトが生成される
  • それらがイベントループに登録される

を行う

概念図
1プロセス
└── asyncio
└── Event Loop
├── Task(Request A コルーチン)
├── Task(Request B コルーチン)
└── Task(Request C コルーチン)

コルーチン内でI/O待ちでawaitすると、

  • 他のリクエストが進む

ので、並行処理の単位は「リクエスト単位のタスク」となる
※リクエストA,B,Cが並行実行される

FastAPIのエンドポイント関数とコルーチンオブジェクトの関係

endpoints.py
@app.get("/")
async def read():
return {"ok": True}

のようなエンドポイント関数があった場合

FastAPIは

  • エンドポイント関数を呼び出す
  • コルーチンオブジェクトを得る
  • リクエストごとに生成されたコルーチンは、ASGIサーバ経由でイベントループ上のTaskとして実行される。

つまり、 FastAPIのエンドポイントはコルーチン関数であり、
リクエストごとに生成されるコルーチンオブジェクトが並行処理の実行単位になる。

Pythonのコルーチンはawaitつけないと実行されないので
このエンドポイント関数は実はFastAPI自身というより、ASGIサーバ側が内部的にawaitし実行している。

並行処理の単位

プロセスの管理はFastAPIではない

FastAPI は アプリケーションなので、OSプロセスを管理する責務は持たない。

プロセス管理を行うのは:

  • Uvicorn
  • あるいは Gunicorn などのプロセスマネージャ

つまり

  • プロセスの生成・管理はサーバ側の責務

FastAPIはその中で動く「ASGIアプリ」でしかない

並行処理の単位は1プロセス 1イベントループ

bash
1プロセス
└── asyncio
└── Event Loop
├── Task(Request A コルーチン)
├── Task(Request B コルーチン)
└── Task(Request C コルーチン)
2プロセス
└── asyncio
└── Event Loop
├── Task(Request A コルーチン)
├── Task(Request B コルーチン)
└── Task(Request C コルーチン)
  • 各プロセスが独自のイベントループを持つ
  • 並行処理はそのイベントループ内で行われる
  • メモリはプロセス間で共有されない

※高度なカスタム構成を除けば、基本は「1プロセス1イベントループ」。

つまり、並行処理は「1プロセス1イベントループ」内で行われる。
上の例だと1プロセス数と2プロセスは別の並行処理になる

起動時にプロセス数は設定できる

uvicornであれば、起動時のコマンドでworker数を指定すればプロセス数を指定できる

bash
uvicorn app:app --workers 4

この場合

  • 4つのOSプロセスを起動する
  • 各プロセスにイベントループがある
  • 各プロセスが独立してリクエストを処理する
  • 4つのプロセスがそれぞれ並行処理を実施する

整理すると

  • プロセスの管理はFastAPIではなくASGIサーバが行う。
  • 通常は「1プロセスに1イベントループ」が存在し、そのイベントループ内でリクエスト単位のコルーチンが並行処理される。
  • 起動時にワーカー数を指定することで、プロセス数を増やすことができる。

Uvicornの起動については下記記事でも解説してます

誤解しやすいポイント

最後に誤解しやすいポイントをまとめてく

asyncにすれば速くなる

  • I/O待ちがある場合のみ有効
  • I/O待ちがない処理の場合は、並行処理にならないので早くならない。
  • CPUバウンド処理は改善しない

まとめ

FastAPIの並行処理はASGI仕様に基づき

  • Uvicornがリクエストを受け取る
  • asyncioイベントループがコルーチンを管理する
  • リクエスト単位でスケジューリングする

ことで成立している。

実際にFastAPIを使ったREST APIの実装例に関しては下記記事で紹介しています

新着記事

top