zig-async - 背景
歴史的経緯
非同期プログラミングの発展
- コールバック時代(1990年代)
- Promise/Future(2000年代)
- async/await(2010年代)
- 構造化並行性(2020年代)
Zigの非同期アプローチ
// Zigは「カラーレス」な非同期を目指す
// 同じコードが同期/非同期両方で動作
fn fetch(url: []const u8) ![]u8 {
// この関数は呼び出し元のコンテキストに応じて
// 同期的にも非同期的にも動作可能
}
コンピュータサイエンス的な意味
I/Oモデルの比較
| モデル | 特徴 | 使用例 |
|---|---|---|
| 同期ブロッキング | シンプル、1リクエスト/スレッド | 従来のサーバー |
| 同期ノンブロッキング | ポーリング必要 | 組み込み |
| 非同期ブロッキング | select/poll | 古いサーバー |
| 非同期ノンブロッキング | イベント駆動 | 現代のサーバー |
イベントループのアーキテクチャ
┌─────────────────────────────────────┐
│ イベントループ │
│ ┌─────────┐ ┌─────────┐ │
│ │タイマー │ │ I/O │ │
│ │キュー │ │キュー │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ └─────┬──────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 実行 │ │
│ │ (コールバック)│ │
│ └─────────────┘ │
└─────────────────────────────────────┘
コルーチンとスタック
スタックフルコルーチン:
- 独自のスタックを持つ
- コンテキストスイッチが重い
- 例: goroutine, Lua coroutine
スタックレスコルーチン:
- ステートマシンに変換
- 軽量
- 例: Zig async, Rust async
実践での活用
Webサーバー
// 複数の接続を効率的に処理
pub fn handleConnections(server: *Server) !void {
while (true) {
// 非同期で接続を受け入れ
const conn = try server.accept();
// 各接続を非同期で処理
_ = async handleClient(conn);
}
}
fn handleClient(conn: *Connection) !void {
defer conn.close();
const request = try conn.readRequest();
const response = try processRequest(request);
try conn.writeResponse(response);
}
データパイプライン
// 非同期ストリーム処理
pub fn processStream(input: *Stream) !void {
while (try input.next()) |data| {
// 変換
const transformed = try transform(data);
// 並行して出力
_ = async output.write(transformed);
}
}
マイクロサービス
┌────────┐ ┌────────┐ ┌────────┐
│Service │────▶│Gateway │────▶│Service │
│ A │ │(async) │ │ B │
└────────┘ └────────┘ └────────┘
│
▼
┌────────┐
│Service │
│ C │
└────────┘
ゲートウェイは全サービスへの
リクエストを並行して処理
実世界とのギャップ
Zigの非同期の現状
Zig 0.11+ では async/await が一時的に無効化されています:
// 現在のZigでは使用不可
// const result = async someFn();
// 代替: std.Thread を使用
const thread = try std.Thread.spawn(.{}, someFn, .{});
thread.join();
代替アプローチ
// I/O多重化
const std = @import("std");
pub fn main() !void {
var poller = try std.io.poll.Poller.init(.{});
defer poller.deinit();
// ファイルディスクリプタを登録
poller.add(fd1, .{ .read = true });
poller.add(fd2, .{ .read = true });
// イベントを待機
const events = try poller.poll(timeout);
for (events) |event| {
// イベント処理
}
}
スレッドプールパターン
const ThreadPool = struct {
workers: []std.Thread,
queue: Queue(Task),
pub fn submit(self: *ThreadPool, task: Task) void {
self.queue.push(task);
}
};
// ワーカースレッド
fn worker(pool: *ThreadPool) void {
while (true) {
const task = pool.queue.pop();
task.run();
}
}