JavaScript/Atomics
Atomics
オブジェクトは、共有メモリ上での原子操作を提供するグローバルオブジェクトです。このオブジェクトは、マルチスレッド環境での同期プリミティブとデータの安全な共有を可能にします[1]。
概要
Atomics
オブジェクトは、共有メモリ上でのデータ競合を防ぐための原子操作を提供します。これらの操作は不可分であり、途中で他のスレッドが介入することはありません。Atomics
には、メモリの読み書き、加算や減算などの数値操作、そして同期のための待機操作と通知操作が含まれています。
静的メソッド
Atomics
オブジェクトは以下の静的メソッドを提供します:
メソッド | カテゴリ | 解説 |
---|---|---|
Atomics.add() | 数値操作 | 指定された位置の値に数値を原子的に追加し、元の値を返します。 |
Atomics.and() | 数値操作 | 指定された位置の値に対して原子的にビット単位のAND演算を行い、元の値を返します。 |
Atomics.compareExchange() | メモリ操作 | 条件付きで値を原子的に置き換えます。 |
Atomics.exchange() | メモリ操作 | 値を原子的に置き換え、元の値を返します。 |
Atomics.isLockFree() | その他 | 指定されたバイトサイズの原子操作がロックフリーであるかどうかを返します。 |
Atomics.load() | メモリ操作 | 指定された位置から値を原子的に読み込みます。 |
Atomics.notify() | 同期操作 | 待機中のエージェントを起こします。 |
Atomics.or() | 数値操作 | 指定された位置の値に対して原子的にビット単位のOR演算を行い、元の値を返します。 |
Atomics.pause() | その他 | ループ内でパフォーマンスを向上させるためのヒントをプロセッサに提供します。 |
Atomics.store() | メモリ操作 | 指定された位置に値を原子的に書き込みます。 |
Atomics.sub() | 数値操作 | 指定された位置の値から数値を原子的に減算し、元の値を返します。 |
Atomics.waitAsync() | 同期操作 | 非同期バージョンの wait メソッドです。 |
Atomics.wait() | 同期操作 | 指定された位置の値が特定の値になるまで待機します。 |
Atomics.xor() | 数値操作 | 指定された位置の値に対して原子的にビット単位のXOR演算を行い、元の値を返します。 |
以下、補足情報です。
- 原子操作 (Atomic Operations):
- 複数のスレッドが同時にメモリにアクセスする際に、データ競合を防ぎ、一貫性を保つための操作です。
Atomics
オブジェクトは、共有メモリ上での原子的な操作を提供します。
- 同期操作 (Synchronization Operations):
- 複数のスレッド間の実行順序を調整し、協調動作を可能にするための操作です。
Atomics.wait()
とAtomics.notify()
は、共有メモリの状態に基づいてスレッドを一時停止および再開するために使用されます。
- ロックフリー (Lock-free):
- ロックを使用せずに、常に少なくとも1つのスレッドが進行できることを保証するプログラミング技法です。
Atomics.isLockFree()
は、特定のサイズの原子操作がロックフリーで実行できるかどうかを判定します。
- メモリ操作 (Memory Operations):
- メモリに対する読み書き操作です。
Atomics
オブジェクトのメソッドを使用することで、複数のスレッドから安全に読み書きを行う事が出来ます。
例
原子的なカウンターの実装
以下のプログラムは、Atomics
を使用して複数のワーカー間で共有されるカウンターを実装します。
// メインスレッド const buffer = new SharedArrayBuffer(4); const counter = new Int32Array(buffer); counter[0] = 0; // ワーカースレッドに共有バッファを渡す const worker1 = new Worker('worker.js'); const worker2 = new Worker('worker.js'); worker1.postMessage(buffer); worker2.postMessage(buffer); // worker.js の内容 self.onmessage = function(event) { const sharedBuffer = event.data; const counter = new Int32Array(sharedBuffer); // カウンターを原子的にインクリメント const oldValue = Atomics.add(counter, 0, 1); console.log(`カウンター値を ${oldValue} から ${oldValue + 1} に更新しました`); };
このプログラムでは、Atomics.add()
を使用して複数のワーカー間で共有される counter
の値を安全にインクリメントしています。原子操作により、データ競合が防止されます。
スレッド間の同期
以下のプログラムは、Atomics.wait()
と Atomics.notify()
を使用してスレッド間の同期を実装します。
// メインスレッド const buffer = new SharedArrayBuffer(4); const int32 = new Int32Array(buffer); int32[0] = 0; const worker = new Worker('waiter.js'); worker.postMessage(buffer); // 2秒後にワーカーに通知を送る setTimeout(() => { Atomics.store(int32, 0, 123); Atomics.notify(int32, 0, 1); console.log('ワーカーに通知を送りました'); }, 2000); // waiter.js の内容 self.onmessage = function(event) { const sharedBuffer = event.data; const int32 = new Int32Array(sharedBuffer); console.log('待機を開始します...'); const result = Atomics.wait(int32, 0, 0); console.log(`待機が終了しました。結果: ${result}`); console.log(`新しい値: ${Atomics.load(int32, 0)}`); };
このプログラムでは、ワーカースレッドが Atomics.wait()
を使用してメインスレッドからの通知を待機します。メインスレッドは値を変更し、Atomics.notify()
を使用してワーカーに通知を送ります。
注意点
Atomics
の操作はSharedArrayBuffer
のビューに対してのみ使用できます。- セキュリティ上の理由から、
SharedArrayBuffer
とAtomics
は、特定のHTTPヘッダー(Cross-Origin-Opener-Policy: same-origin
およびCross-Origin-Embedder-Policy: require-corp
)が設定されている場合にのみ利用可能です。 Atomics.wait()
はメインスレッドでは使用できませんが、Atomics.waitAsync()
は使用できます。
脚註
- ↑ これは、
SharedArrayBuffer
オブジェクトと組み合わせて使用され、スレッド間の安全なデータ共有を実現します。