The missing standard library for multithreading in JavaScript

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()
      を使用して制御します。
    • RwLockSemaphore、および Condvar はそれぞれ非同期メソッド(
      acquire
      read
      write
      wait
      )とブロッキングバリアントを提供し、ワーカーのデッドロックを回避します。
  • チャネル:
    channel<T>(capacity)
    SharedJsonBuffer
    をバックエンドに持つ有界 MPMC チャネルを作成します。
    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()

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
spawn(fn)

spawn(move(...), fn)
ワーカーで関数を実行
initRuntime(config?)
スレッドプールを遅延初期化
shutdown()
すべてのワーカーを終了
Memory Management
move(...args)
引数の所有権を転送
drop(resource)
リソース(ロック等)を手動で破棄
SharedJsonBuffer
JSON オブジェクトを共有メモリに格納
Channels
channel<T>(capacity?)
[Sender<T>, Receiver<T>]
MPMC チャネル作成
Sender<T>
メソッド:
send()
,
sendSync()
,
clone()
,
close()
Receiver<T>
メソッド:
recv()
,
recvSync()
,
clone()
,
close()
Synchronization
Mutex<T>
:
acquire()
,
tryLock()
,
acquireSync()
RwLock<T>
:
read()
,
write()
,
readSync()
,
writeSync()
Semaphore
:
acquire(amount?)
,
tryAcquire(amount?)
,
acquireSync(amount?)
Condvar
:
wait(guard)
,
notifyOne()
,
notifyAll()
,
waitSync(guard)

技術的実装詳細

  • Serialization Protocol – “Envelope” 形式 (
    PayloadType.RAW
    /
    LIB
    ) を用い、ワーカー内で Mutex 等を再構築
  • Atomics
    Int32Array
    SharedArrayBuffer
    )上に構築し、
    Atomics.wait/notify
    を活用
  • Import Patching
    spawn
    は呼び出し元のスタックトレースを解析し、正しい相対パス解決のために
    import()
    呼び出しを正規表現で修正

これで JavaScript アプリケーションに安全で強力な並列処理を導入できます。

multithreading.js
をぜひお試しください!

同じ日のほかのニュース

一覧に戻る →

2025/11/30 18:11

Self-hosting my photos with Immich

## Japanese Translation: 記事では、著者が低電力Ryzen 7ミニPC(ASRock DeskMini X600)に64 GB RAM、1 TBディスクを搭載し、アイドル時の消費電力が10 W未満である環境にImmichをセットアップした手順を説明しています。Proxmox上に「photos」という名前のVMを作成し、500 GBのストレージ、4つのCPUコア、4 GB RAMを割り当てました。NixOS設定ファイルで `services.immich.enable = true` を有効化してImmichサービスを起動します。このサービスは `tailscale serve --bg http://localhost:2283` コマンドと MagicDNS/TLS によりTailscale経由で公開され、`https://photos.example.ts.net` からアクセス可能です。 公式の `immich‑cli` を使用した初期写真インポートでは、バックグラウンドジョブがタイムアウトし、Google Takeout のJSONメタデータが無視されるという問題が発生しました。第三者ツール **immich-go** が両方の問題を解決します。`immich-go upload from-google-photos …` を実行することでバックグラウンドタスクを一時停止し、Google Takeout アーカイブを正しく処理できます。その後、iPhoneアプリはTailscale URL経由でログインし、自動アップロードが有効化され、通知は無効にしてアップロードアラートを防止します。 バックアップについては、著者はsystemdタイマーを使用して `/var/lib/immich`(UPLOAD_LOCATION)ディレクトリ全体を rsync で3‑2‑1戦略で同期する予定です。これはImmichの公式ドキュメントに従った方法です。記事では、Immichには組み込みの写真編集機能がないため、ユーザーはGIMPなど外部ツールを使用しなければならず、共有もまだGoogle Photos経由で行われると指摘しています。Enteと比較して、著者は既存のTailscale VPNとLUKSディスク暗号化が十分なセキュリティを提供するため、エンドツーエンド暗号化を必要としないImmichを好んでいます。 総じて、このセットアップは小型かつ省電力マシン上で高速で信頼性の高いセルフホスト写真保存ソリューションを実現しており、外部編集ワークフローを受け入れられる趣味家や小規模ビジネスに適しています。

2025/12/06 12:32

Nook Browser

## Japanese Translation: > **概要:** > 製品「Browse」はプライバシーを最優先としたオープンソースのウェブブラウザで、ユーザーのデータが販売または追跡されることは決してないと約束しています。WebKit エンジンをベースに構築されており、高速なパフォーマンスと最小限のシステムオーバーヘッドを実現しながら、インターフェイスはクリーンで侵入的なポップアップがありません。チャット支援や要約、最新のウェブ情報などの AI 機能は、ユーザーが明示的に選択した場合のみ利用可能です。コードベース全体が公開されており、パーミッシブ ライセンスでリリースされています。また、コミュニティ主導のロードマップに従い、新しいツールを追加する前に安定性を優先しています。設定はユーザーが理解しやすく、逆行可能(戻せる)ように設計されています。FAQ セクションでは、これらのポイント以外に独自の情報は提供されていません。 このバージョンは主要なポイントをすべて保持し、業界への影響についての推測を削除し、設定の逆行性と FAQ の内容に関する欠落した詳細を追加しています。

2025/12/06 0:35

Cloudflare outage on December 5, 2025

## Japanese Translation: ``` ## Summary Cloudflare の 2025 年 12 月 5 日の障害は約 25 分間続きました。 08:47 UTC にネットワークセグメントが故障を開始し、08:50 UTC に完全な影響に達し、09:12 UTC に問題が解決しました。 全 HTTP トラフィックの約 28 %(古い FL1 プロキシと Managed Rulesets を使用している顧客)がエラーを経験しましたが、中国ネットワークトラフィックは影響を受けませんでした。 障害は、CVE‑2025‑55182(React Server Components の脆弱性)を修正するために意図された二つの急速なコード変更によって引き起こされました。 まず、WAF バッファサイズが 128 KB から 1 MB に増加し、段階的に展開されました。 次に、グローバル設定更新で内部 WAF テストツールが無効化され、FL1 のルールモジュールで Lua エラー(`attempt to index field 'execute' (a nil value)`)を引き起こし、HTTP 500 応答を生成しました。 このバグは何年も存在していましたが、「execute」ルールのキースイッチが execute フィールドが欠落した際に処理するコードを回避したために露呈しました。 同様で大規模なインシデントが 2025 年 11 月 18 日にも発生しました。 Cloudflare はロールアウト手順の強化、バージョン管理制御の追加、ブレイクグラスアクセスの簡素化、およびフェイルオープンエラーハンドリングの実装に取り組んでいます。詳細なレジリエンシー計画は来週公開される予定であり、新しい緩和策が稼働するまでネットワーク変更は停止されたままです。 この出来事は、大規模 CDN 運用における厳格な変更管理の必要性を強調し、迅速展開保護策に関する業界全体での見直しを促す可能性があります。 ```

The missing standard library for multithreading in JavaScript | そっか~ニュース