
2025/12/06 6:09
The missing standard library for multithreading in JavaScript
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
改良された概要
Multithreading.js は、Rust スタイルの並行性プリミティブ(Mutex、RwLock、セマフォ、および条件変数)を JavaScript に導入する TypeScript ライブラリです。WebWorkers と SharedArrayBuffers を活用して実装されており、機械のハードウェア同時実行性に合わせたワーカープールを作成し、ワーカー内で相対インポートを自動的に修正します。また、効率的なデータ転送のためにゼロコピー移動セマンティクスを使用しています。主な機能としては、
SharedJsonBuffer があり、JSON オブジェクト全体をコピーせずに高速な部分更新が可能です。
コア API のハイライト
- インストール:
npm install multithreading - ランタイム制御: ワーカープールの設定には
を使用し、クリーンに終了させるにはinitRuntime(config)
を呼び出します。shutdown() - タスク実行:
はワーカー内で関数を実行し、spawn(fn)
は引数を移動セマンティクスで転送またはクローンします。spawn(move(arg1,…), fn) - 同期プリミティブ:
- Mutex – 非同期ガード(
)や手動のusing
を使用して制御します。drop() - RwLock、Semaphore、および Condvar はそれぞれ非同期メソッド(
、acquire
、read
、write
)とブロッキングバリアントを提供し、ワーカーのデッドロックを回避します。wait
- Mutex – 非同期ガード(
- チャネル:
はchannel<T>(capacity)
をバックエンドに持つ有界 MPMC チャネルを作成します。SharedJsonBuffer
、send
、クローン、およびすべてのハンドルがドロップされたときに自動で閉じる機能をサポートしています。recv - インポート処理: スパーンは相対インポートを自動的に修正し、ワーカーが外部 npm パッケージやローカルファイルをパスの問題なくロードできるようにします。
設計目標と影響
このライブラリは、同期プリミティブよりも非同期プリミティブを使用してワーカースレッドを応答性高く保つことを奨励しています。Rust の既知の並行パターンを JavaScript に露出することで、開発者はブラウザまたは Node.js で最小限のボイラープレートで高度に同時実行的なコードを書けるようになり、データ処理、リアルタイム分析、およびサーバー側並行性に依存していたマルチユーザーアプリケーションなど、CPU 集中型タスクのパフォーマンスを向上させます。
本文
Multithreading.js – 安全で堅牢な WebWorker 並列処理を実現する TypeScript ライブラリ
概要
multithreading は Rust の並列性プリミティブを JavaScript に移植したライブラリです。
- スレッドプールアーキテクチャ
- 厳格なメモリー安全セマンティクス
- 同期プリミティブ(Mutex、RwLock、Semaphore、Condvar)
WebWorker、シリアライズ、および
SharedArrayBuffer の複雑さを隠蔽し、通常の非同期 JavaScript と同じ感覚でマルチスレッドコードを書けます。
インストール
npm install multithreading
コアコンセプト
| 機能 | 説明 |
|---|---|
| Managed Worker Pool | ハードウェア並列度に応じて自動スケーリング |
| Shared Memory Primitives | ランク条件を排除した安全な状態共有 |
| Scoped Imports | ワーカータスク内で外部モジュールや相対ファイルを直接インポート |
| Move Semantics | クローンオーバーヘッドを回避するための明示的データ所有権転送 |
速習例
import { spawn } from "multithreading"; // バックグラウンドスレッドでタスクを生成 const handle = spawn(() => { const result = Math.random(); return result; }); // 結果待ち const result = await handle.join(); if (result.ok) { console.log("Result:", result.value); // 例: 0.6378467071314606 } else { console.error("Worker error:", result.error); }
データ転送:move()
move()WebWorker は外部スコープの変数をキャプチャできないため、
move() を使ってデータを渡します。
import { spawn, move } from "multithreading"; const largeData = new Uint8Array(10 * 1024 * 1024); // 10 MB const metaData = { id: 1 }; const handle = spawn(move(largeData, metaData), (data, meta) => { console.log("Processing ID:", meta.id); return data.byteLength; }); await handle.join();
- Transferable Objects – ゼロコピーで所有権を転送
- Non‑Transferable Objects –
によってクローンstructuredClone
Shared JSON バッファ
import { move, Mutex, SharedJsonBuffer, spawn } from "multithreading"; const sharedState = new Mutex( new SharedJsonBuffer({ score: 0, players: ["Main Thread"], level: { id: 1, title: "Start" }, }) ); await spawn(move(sharedState), async (lock) => { using guard = await lock.acquire(); const state = guard.value; console.log(`Current Score: ${state.score}`); state.score += 100; state.players.push("Worker1"); }).join(); using guard = await sharedState.acquire(); console.log(guard.value); // { score: 100, players: ["Main Thread", "Worker1"], ... }
同期プリミティブ
1. Mutex
import { spawn, move, Mutex } from "multithreading"; const buffer = new SharedArrayBuffer(4); const counterMutex = new Mutex(new Int32Array(buffer)); spawn(move(counterMutex), async (mutex) => { using guard = await mutex.acquire(); // スコープ終了時に自動解放 guard.value[0]++; });
- 自動 vs 手動管理
(推奨)を使用。using
が利用できない環境ではusing
内でtry…finally
を呼びます。drop()
2. RwLock
import { spawn, move, RwLock } from "multithreading"; const lock = new RwLock(new Int32Array(new SharedArrayBuffer(4))); spawn(move(lock), async (l) => { using guard = await l.write(); guard.value[0] = 42; }); spawn(move(lock), async (l) => { using guard = await l.read(); console.log(guard.value[0]); });
3. Semaphore
import { spawn, move, Semaphore } from "multithreading"; const semaphore = new Semaphore(3); for (let i = 0; i < 10; i++) { spawn(move(semaphore), async (sem) => { using _ = await sem.acquire(); // 許可が無ければ待機 console.log("Acquired slot! Working..."); await new Promise(r => setTimeout(r, 1000)); }); }
4. Condvar
import { spawn, move, Mutex, Condvar } from "multithreading"; const mutex = new Mutex(new Int32Array(new SharedArrayBuffer(4))); const cv = new Condvar(); spawn(move(mutex, cv), async (lock, cond) => { using guard = await lock.acquire(); while (guard.value[0] === 0) { await cond.wait(guard); // ロックを解放して待機、再取得 } console.log("Received signal, value is:", guard.value[0]); });
チャネル(MPMC)
SharedJsonBuffer に裏付けられた境界付きマルチプロデューサ/マルチコンシューマキュー。
import { spawn, move, channel } from "multithreading"; const [tx, rx] = channel<{ hello: string }>(); // デフォルト容量は1 // プロデューサー spawn(move(tx), async (sender) => { await sender.send({ hello: "world" }); await sender.send({ hello: "multithreading" }); // GC 時に自動クローズ }); // コンシューマー spawn(move(rx.clone()), async (receiver) => { for await (const value of receiver) { console.log(value); // { hello: "world" }, 次に { hello: "multithreading" } } });
主な特徴
- 任意の JSON データをサポート
- バウンド容量とバックプレッシャー
- クローン可能、参照カウンタ付きハンドル
ワーカー内でモジュールをインポート
ワーカーは
await import() で npm パッケージおよび相対ファイルを自動的に解決します。
import { spawn } from "multithreading"; spawn(async () => { const utils = await import("./utils.ts"); // main.ts に対する相対パス const _ = await import("lodash"); console.log("Magic number:", utils.magicNumber); console.log("Random number via lodash:", _.default.random(1, 100)); });
API リファレンス
| カテゴリ | 関数 / クラス | 説明 |
|---|---|---|
| Runtime | | ワーカーで関数を実行 |
| スレッドプールを遅延初期化 | |
| すべてのワーカーを終了 | |
| Memory Management | | 引数の所有権を転送 |
| リソース(ロック等)を手動で破棄 | |
| JSON オブジェクトを共有メモリに格納 | |
| Channels | → | MPMC チャネル作成 |
メソッド: , , , | ||
メソッド: , , , | ||
| Synchronization | : , , | |
: , , , | ||
: , , | ||
: , , , |
技術的実装詳細
- Serialization Protocol – “Envelope” 形式 (
/PayloadType.RAW
) を用い、ワーカー内で Mutex 等を再構築LIB - Atomics –
(Int32Array
)上に構築し、SharedArrayBuffer
を活用Atomics.wait/notify - Import Patching –
は呼び出し元のスタックトレースを解析し、正しい相対パス解決のためにspawn
呼び出しを正規表現で修正import()
これで JavaScript アプリケーションに安全で強力な並列処理を導入できます。
multithreading.js をぜひお試しください!