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

ジグの新しい非同期入出力

2025年10月29日原文(andrewkelley.me)

概要

Zig 0.16.0で導入される 新しいstd.Io非同期I/Oプリミティブ の概要と使い方を紹介。 async/await、キャンセル、リソース管理 などの基本APIを例を通じて解説。 スレッドベースのIo実装 を使いながら、同期・非同期処理の違いを体験。 エラー処理やリソースリーク対策 の実践的なパターンも説明。 今後のZigアプリ開発で役立つ 標準的な非同期I/O設計 のヒントを提供。


Zig 0.16.0の新しいstd.Io非同期I/O入門

  • Zig 0.16.0で std.Ioの非同期I/O API が刷新
  • 3-4ヶ月後リリース予定、先行してZigtoberfest 2025でデモ実施
  • async/awaitやキャンセル、同期API など、全Zigコードで利用可能なコアAPIを解説
  • まずは 基本から順に発展的な非同期処理 へステップアップ

例0:基本的な同期処理

  • Zigでの 「Hello, World!」的な同期I/O例
  • doWork()関数で1秒スリープしつつ 標準出力に文字列表示
  • 非同期処理は未使用、 I/Oの基本動作の確認

例1:Ioインスタンス導入

  • std.Io実装(Threaded)をmainで初期化
  • Allocatorパラメータ もセットアップし、推奨設計パターンを紹介
  • I/Oやアロケーションが必要なコードはパラメータで受け取る 設計
  • まだ 非同期処理は未使用、I/O実装のセットアップに注力

例2:async/awaitの基本

  • io.async/doWorkで非同期タスク生成
  • future.await(io) で結果を待つ
  • 呼び出しと復帰を分離 できるのがasync/awaitの意義
  • ただし、即座にawaitしているため 挙動は同期と同じ

例3:並列実行の表現

  • 2つの非同期タスクを同時に起動
  • 各タスクに異なる引数(flavor_text)を付与
  • awaitを順次呼び出しても、実際は同時進行
  • 1秒で2つの作業が完了、非同期I/Oのメリットを実感

例4:エラー処理とリソースリーク

  • doWork内で条件によりエラー(OutOfMemory)発生
  • try a.await(io) でエラー発生時、 b.await(io) がスキップされる
  • その結果、 リソースリークが発生 し、デバッグアロケータが検出
  • 典型的な非同期エラー処理の落とし穴

例5:リソースリーク対策の基本形

  • awaitの結果をすべて受け取ってからtryでエラー判定
  • 全タスクのリソース開放が保証される
  • エラーは適切にハンドリング可能、 ただし記述が冗長でフットガン

例6:キャンセルによる最適化と安全性

  • deferでタスクのキャンセル(a.cancel(io))を自動実行
  • エラー発生時に即座にキャンセルが走り、リソースリーク防止
  • cancelはawaitと同じAPIで、両者は冪等
  • 各I/O実装ごとにキャンセルの挙動が定義される

例7:リソース管理の実践例

  • doWorkが成功時にヒープ上に文字列を返却
  • defer if (a.cancel(io)) |s| gpa.free(s) で確実にリソース開放
  • 成功/失敗問わず、リソース管理を簡潔に記述
  • 標準的なtry/returnスタイルで記述可能

例8以降:非同期=並行ではない

  • asynchrony(非同期)はconcurrency(並行)と異なる概念
  • キューやプロデューサ・コンシューマパターン など、現実的な非同期制御例も今後紹介予定

まとめと今後

  • std.Ioの新APIで、より安全かつ効率的な非同期I/Oが可能
  • キャンセルやリソース管理の仕組み により、従来の落とし穴を回避
  • async/awaitの活用で、表現力豊かなZigコード が書ける
  • 今後のZig 0.16.0のアップデートに期待

参考資料

Hackerたちの意見

アンドリュー・ケリーは僕のお気に入りの技術スピーカーの一人で、Zigは素晴らしいアイデアが詰まってる。彼はオープンソースプロジェクトのリーダーとしてもいいお手本みたいだね。

Zigは素晴らしいアイデアでいっぱいなだけじゃなくて、捨てられたアイデアの墓場もあるし、すぐに却下されたアイデアの宇宙もあると思う。Zigは、うまく組み合わさる素晴らしいアイデアがたくさん詰まってるよ。

これが完全にオプションであってほしいけど、現実的にはそうならないだろうな。Rustみたいに、オプショナルな非同期サポートがある言語は、非同期がエコシステム全体に浸透しちゃうからね。色付きの関数があると、そうなるのは予想通りだよ。標準ライブラリはそこまで悪くないけど、前にチェックしたときは、実際にはブロックしないのに非同期関数がたくさんあったんだ。非同期は多くの人にとってうまく機能してるのは分かるし、スレッドを理解できない人が非同期を好むのも理解できる。生産的になれるパターンがあるのは素晴らしいよね!でも、僕には非同期はどうしても合わない。使うのが不安で、もう10年以上試行錯誤してるけど、結局うまくいかないかも。スレッド、ミューテックスロック、チャネル、Erlangスタイルの並行処理、ナーサリー、非同期以外の何でもの方がずっと楽だな。これらは全部理解できるし、実際にそれらを使ってプロダクションシステムも作ったことがある。Zigが1.0に達したときには使えるようになってるといいな。今月初めに学び始めたけど、使うのがすごく楽しいよ。

スレッドを理解できない人が非同期を好むのも理解できる。それはお互いに独立してるよ。スレッドがあってもなくても非同期は使えるし、非同期があってもなくてもスレッドは使える。

同意だな、非同期は本来あるべきよりも人気がありすぎる。少なくとも(私の理解では)Zigは関数に色を付けないから、ブロッキングと非同期ライブラリの間に大きなエコシステムの亀裂は生まれないだろうね。

スレッドの方がずっと楽だな。動画の最初の数分で示されたサンプルコードは、実際には非同期コードを実行するために通常のOSスレッドを使ってるよ ;) これ全体はZigのアロケーター哲学にかなり似てる。アプリケーションがライブラリに渡すためにルートアロケーターを選ぶのと同じように、IOの実装も選んで渡すんだ。ライブラリはIOシステムが非同期をどう実装してるかは気にしないで、アプリケーションから渡されたIO実装を呼び出すだけなんだ。

同じく。asyncは好きじゃないんだ。コードの各行の前に「await」を付けるのが面倒で。最近は(JSで)ワーカースレッドやメッセージパッシング、"atomics" APIを使って遊んでる。並行処理の利点を得られるし、async/awaitの煩わしさがないからね。

スレッドのことは理解してるけど、特定のことにはasyncを使うのが好きなんだ。もしスレッドを使ったウェブサービスがあったら、リクエストごとにスレッドプールの一つのスレッドに割り当てるのかな?IOの多重化がOSスレッドなしでできるのに、OSリソースを無駄にしてる気がする。> 最後にチェックしたとき、crates.ioの多くは実際にはブロックしないもののためのasync関数で埋まってたよ。具体的には?大きなファイルのファイルI/Oは遅いデバイスではブロックするから、asyncのtarball処理には使い道があるよね。sans-IOスタイルで書いて、その上にスレッドやasyncを薄い層として乗せるのがベストだと思う。でも実際には、そこそこ使えるsans-IOコードを書くのは、そこそこ使えるasyncを書くより難しいと感じる。HTTPライブラリのような深い間接依存には理にかなってるけど、アプリにはあまり合わないかな。

スレッドを理解できない人の気持ちは完全にわかるし、asyncを好む人もいるよね。これは奇妙な発言だね。async/awaitは「スレッドが理解できないときのためのもの」じゃなくて、全く別の概念だよ。例を挙げると、JavaScriptにはasync/awaitがあるけど、全てがシングルスレッドで、並列処理はない。async/awaitは基本的にコルーチン/ジェネレーターの下にあるものだよ。「スレッドが理解できない人のためのもの」と言うと、まるでasyncの仕組みを学んでないことに不安を感じているみたいに聞こえるし、ただ座って学ぶ代わりに補おうとしてるみたいに聞こえる。asyncはスレッドやファイバーよりも並行処理を表現するための複雑なモデルかもしれない。それを言うのはいいけど、学んでないのも問題ないし、片方をもう片方より上に置くのはおかしいよ。> stdlibはそんなに悪くないけど、最後にチェックしたとき、crates.ioの多くは実際にはブロックしないもののためのasync関数で埋まってたよ。例を挙げてもらえる?最後にRustを使ったときはそうは思わなかったけど、最近はあまり使ってないんだ。

あなたのasyncに対する問題は、並行性と並列性を混同してることのように聞こえるね。

期待しなくていいよ。関数の色を避けて、IOがasyncかどうかに依存しないライブラリを書くことが、この新しいIO実装の最優先事項の一つなんだ。async/awaitを使いたくないなら、io.asyncを通して関数を呼び出さなければいいだけだよ。

スレッドを理解できない人がasyncを好む Wow。そんなコメントの後に誰かが読み続けると思う?

Hacker Newsで議論の続きを見る