
asyncioはPython 3.4で2014年に標準ライブラリに加わった非同期I/Oフレームワークで、Guido van Rossumが「Tulip」プロジェクトとして主導しPEP 3156で形を整えた。イベントループ、コルーチン、Future、TaskといったプリミティブをPython本体に組み込み、Twistedや独自のIOLoopに頼らずに非同期プログラムを書けるようにした点が大きな意義である。本稿ではasyncioの成り立ち、コア概念、主要API、運用上の注意点を順に整理する。
この記事の目次
- asyncio登場までの経緯
- イベントループとコルーチンの関係
- ネットワーク・サブプロセス・同期プリミティブ
- 周辺ライブラリとエコシステム
- まとめ
asyncio登場までの経緯

Python 2系の時代、非同期I/Oといえば1998年生まれのTwistedや、2000年代後半に登場したgevent、Tornadoなどサードパーティの選択肢に頼っていた。それぞれ流儀が異なり、組み合わせが難しいという課題があった。2012年頃からGuido van Rossumは「Tulip」というプロジェクトを公開し、標準的な非同期モデルをPython本体に組み込むことを目指して仕様策定を進めた。
成果はPEP 3156としてまとめられ、2014年3月にリリースされたPython 3.4でasyncioとして標準入りした。当初は@asyncio.coroutineとジェネレータベースの構文だったが、Python 3.5(2015年)でPEP 492によりasync defとawaitの専用構文が導入され、コードはさらに直感的になった。
イベントループとコルーチンの関係

asyncioの中心はイベントループであり、asyncio.get_event_loop()または3.7以降推奨のasyncio.run()によって起動する。ループはソケットやタイマなどI/Oリソースの準備完了イベントを順に取り出し、登録されたコールバックやコルーチンを再開する。コルーチンはasync defで定義され、await地点でループに制御を返すことで他のタスクに切り替わる。
並行にタスクを走らせるにはasyncio.create_task(coro)を使い、複数の完了を待つにはasyncio.gatherを呼ぶ。タイムアウトはasyncio.wait_for、キャンセルはtask.cancel()で表現する。これらは同期コードのFutureに近い感覚で扱えるため、スレッドベースの並行モデルから移行する際の認知負荷が比較的低い。
ネットワーク・サブプロセス・同期プリミティブ

asyncioはトランスポートとプロトコルの分離をベースに、低レベルAPIとしてloop.create_connectionやloop.create_serverを提供する。高レベルにはStreams APIのasyncio.open_connection、asyncio.start_serverがあり、reader.read()とwriter.write()で読み書きする。サブプロセス制御もasyncio.create_subprocess_execで同様の非同期APIに揃えられている。
同期プリミティブとしてasyncio.Lock、asyncio.Semaphore、asyncio.Queue、asyncio.Eventが用意され、スレッドの世界と似た感覚で資源調停ができる。ただし内部実装はあくまでループ上のスケジューリングであり、GILとの関係やCPUバウンドな処理での限界、run_in_executorによるスレッドプール委譲などは別途理解しておく必要がある。
周辺ライブラリとエコシステム

asyncioを基盤にして発展したライブラリは多い。HTTPクライアント・サーバのaiohttp、ASGIアプリを動かすUvicornやHypercorn、Webフレームワークでは2018年にSebastián Ramírezが公開したFastAPI、低レベルのStarletteなどがその代表である。DB方面ではSQLAlchemy 2.0が非同期APIを正式提供し、asyncpgやaiomysqlも揃っている。
一方で構造化並行性を志向するTrio(2017年Nathaniel Smith開発)や、両者を抽象化するAnyIOといったプロジェクトも存在し、asyncioの設計に対する議論は今も続いている。標準であることの強みと、構造化並行性が後付けである弱みを理解したうえで使い分けるのが現代的な姿勢である。
まとめ
asyncioはPython公式の非同期I/O基盤であり、コルーチンとイベントループに基づく一貫したモデルを提供する。TornadoやTwistedの経験を吸収しつつ標準入りした経緯を踏まえると、新規の非同期コードを書く際はまずasyncio中心に組み、必要に応じてTrioやAnyIOへ広げるのが堅実なアプローチである。
※本記事はIT用語辞典の手書きドラフトです。公開前に最新情報・出典を確認のうえ加筆修正してください。

コメント