世界を動かす技術を、日本語で。

Pythonは非同期処理を10年間持っているのに、なぜもっと人気がないのか?

概要

  • Python 2から3への移行 は当初コミュニティを分裂させたが、最終的には大きな問題とならず
  • Python 3.5でasync/awaitが導入 され、主にWeb開発で活用
  • Python 3.14でFree-Threading(PEP 779)とMultiple Interpreters(PEP 734)が公式サポート
  • asyncはI/Oバウンド処理で特に有効 だが、CPUバウンドやファイルI/Oには制限
  • GILの存在や実装の違い により、asyncや並列処理の普及が限定的

Python 3.14と並行・並列処理の進化

  • Python 3.14 では PEP 779: Free-ThreadingPEP 734: Multiple Interpreters が公式サポート
    • Free-Threading: GILを排除 し、より細かいロックで並列実行を可能に
    • Multiple Interpreters: 標準ライブラリで複数インタプリタを管理 できる機能
  • これらの新機能は Pythonでの並行・並列処理の大きな前進
  • async/awaitは10年以上前から存在 し、主にWeb開発で利用
  • しかし、 主要Webフレームワークでのasync対応は限定的
    • FastAPI: 完全なasync対応
    • Django:一部async対応、ORMは未対応
    • Flask: 同期処理が基本
    • SQLAlchemy: 2023年にasyncio対応

asyncが普及しない理由

  • asyncはI/Oバウンド処理(特にネットワーク)で真価を発揮
    • 複数のHTTPリクエストなど、 同時発行・待機が容易
  • ファイルI/Oやサブプロセス等はasyncioで非同期化困難
    • aiofilesなどのサードパーティ利用時も 内部的にはスレッドプールに依存
    • OSのファイルI/O非同期API(io_uring等)は セキュリティ問題や制限あり
  • 実際にはネットワークI/O以外でのasync活用は限定的
  • 主要なasyncio API一覧
    • Sleep: asyncio.sleep()
    • TCP/UDP Streams: asyncio.open_connection()
    • HTTP: aiohttp.ClientSession()
    • サブプロセス: asyncio.subprocess
    • キュー: asyncio.Queue

GILとasyncの限界

  • GIL(Global Interpreter Lock) があるため、 CPUバウンドな処理は並列化不可
  • C#のasync/awaitモデル とPythonの違い
    • C#: タスクプールで自動的にスレッド管理
    • Python: イベントループが単一スレッドで管理
  • async関数内でblocking処理があると、イベントループ全体がブロック
    • 例:time.sleep()を使うと 全体が止まる
  • I/O処理やスリープ以外はほぼブロックするため、asyncの恩恵が限定的
  • run_in_executor でスレッドプールと組み合わせることも可能だが、 設計が複雑化

Free-Threadingの意義と今後

  • Python 3.13で実験的にFree-Threading(GILなしビルド)が登場
    • 3.14でより安定化、2026年以降に本格導入を想定
  • コルーチンとスレッドの比較
    • コルーチン: メモリフットプリントが小さい、低いコンテキストスイッチコスト
    • スレッド: GILの影響を受けやすい
  • Free-Threading導入でasyncioの役割がどう変わるかは今後の課題
  • 今後10年で新しい並列処理モデルが普及するかは、実装と現場の需要次第

まとめ

  • async/awaitはI/Oバウンド処理で有用だが、用途が限定的
  • GILやOSレベルの制限が普及の障壁
  • Python 3.14の新機能で並列処理の選択肢が大幅に拡大
  • 今後はasync・Free-Threading・Multiple Interpretersの使い分けがポイント
  • 現場のニーズと実装の熟成が普及のカギ

Hackerたちの意見

サイドプロジェクトでasyncioサーバーを書いてた時期があったんだ。特に楽しかったのは、複雑な方法で反応するものを書くことだった。例えば、メッセージキューやDenon HEOS音楽プレーヤーへのTCP接続をリッスンするwebsocketsサーバーとかね。最終的には「画像ソーター」を作ったんだけど、ブラウザが画像を並列でダウンロードしようとするとハングアップしちゃってた。画像の提供はCPUに依存するべきじゃなかったし、sendfile()も使ってたんだけど、他のリクエストがCPUを占有しちゃって、sendfileをセットアップするのに必要な少しのCPUがブロックされてたんだ。だから、aiohttpからFlask APIに切り替えて、FlaskかGunicornで提供することにした。画像処理はMicrosoft IISやnginxを使ってPythonがやらなくてもいいようにしたんだ。ちょっと面倒だけど、Windowsで開発してるからWSL2内でGunicornを動かさなきゃいけないけど、うまくいってるし、サーバーのパフォーマンスについて考えなくて済むようになったよ。

それがイベント駆動サーバーの主な問題だよね。もしワークロードのどれかがCPU集約型だったら、同じスレッドで他のすべての処理がブロックされる可能性があるから、本来はすぐに応答すべきリクエストが実際にはランダムに時間がかかることになる。基本的に、CPU負荷の高い作業があるなら、そのサーバーには入れない方がいい。

あなたが言った問題を「楽しい」と感じる人がいるのは心強いね。Redisのpubsubから読み込むFastAPIのWebSocketを書くのは、ドキュメントなしでの手探り状態だよ。

著者は根本的な問題に近づいてるけど、はっきりとは言ってないね。実際、Pythonのasyncは遅すぎたんだ。導入された時には、すでに多くのI/Oを同時に処理する必要がある人たちは自分たちのワークアラウンド(フォークとか)を持ってたし、必要ない人たちはそれなしでやりくりする方法を見つけてた(マルチプロセッシングとか)。その間に、Goは良いグリーンスレッドの姿を見せてくれたし、Javaもそれをやった。さらに、JSはずっと良いasyncサポートを持ってた。でも、結局それが示したのは、asyncコードはグリーンスレッドコードに比べて単純にクソだってこと。ブロックできるグリーンスレッドコードの方が、asyncのダンスをしなきゃいけないコードよりもずっと良いのに。だから、良い解決策がすでにあったのに、なんでそれに関わる必要があるの?

でも、結局それが示したのは、asyncコードはグリーンスレッドコードに比べて単純にクソだってこと。これに関しては職場でめちゃくちゃ叩かれるけど、100%同意だよ。見た目は同期的だけど実際は非同期なコードは、変な失敗モードや特異性があって、職場のコードのasync部分にはバグが多いのをよく見る。もしかしたら私が古いだけかもしれないけど、そんなのは価値がないと思う。結局、継続やクロージャの上にある文法的な糖衣に過ぎないよ。

それにasyncが深く必要だと組み合わせると、まさにその通りだね。ネットワークスタック全体がasyncファーストである必要があって、人気のあるネットワーキングライブラリはそれに基づいて構築されている必要がある。多くのライブラリはすでにC拡張ベースで、新しいPythonの部分とはうまく噛み合ってないんだ。

プロダクションで結構な量のPythonを書いてメンテしてきた者として、最近はサーバーサイドのTypeScriptもかなり書いてるけど、これが私の答えだよ。最近当たり前になってることを一つ加えたいんだけど、手頃なマルチスレッドCPUがここ10年で本当に普及したよね。グリーンスレッドに基づくスタックは、asyncや非asyncでコードを汚すことなく「ただ動く」し、1つのコンピュートインスタンスをN個のvCPUを持つインスタンスに優雅にスケールさせることができるんだ。

でも、asyncコードはグリーンスレッドコードと比べて本当にクソだってことを示すだけだった。グリーンスレッドのネイティブUIパラダイムが人気になったら納得するけど、良いネイティブUIのストーリーを持ってる言語はみんなasyncサポートがあるみたいだね。

Asyncはコードを汚すし、async/awaitは古典的な協調マルチタスクの問題に陥る。 「これがあれをブロックするってどういうこと?」 高レベルの作業のためのメモリと実行モデルは、asyncを持たない方がいいと思う。Goはユーザーの視点から見て、うまくやっている典型的な例だと思う。

asyncioのAPIデザインはちょっと外してると思う。以前geventを多用してた私からすると、いろんな quirks や粗い部分があって、ちょっと不思議で、逆に生産性を下げてる気がする。

asyncは自体としてはかなり良い「グリーンスレッド」だよ。コルーチンはもっと良くなる可能性があるけど、実際には重なり合った問題を解決してるだけだね。同じ問題もあれば、違う問題もある。JavaScriptのasyncはタスクを整理する良い方法がないけど、これはグリーンスレッドの重要な特徴だよ。Sindre Sorhusはそれに近いライブラリをいくつか持ってるけど、まだ穴がある。コルーチンができることは、命令キャッシュを最適化すること。でも、goroutineがそれを完全に達成できるかはわからない。実装の詳細以外にそれを妨げるものはないけどね。

asyncは何かを考えるのが楽になる方法だと思うけど、たくさんの抜け道を残しちゃうよね。時には書くのが楽なだけなんだけど、その抜け道にはPythonでは提示されない隠れた責任がたくさんあるんだ(所有権みたいな)。本当に技術的に深く入りたい人は、結局Pythonを選ばないと思う。

Hacker Newsで議論の続きを見る