Atomics și SharedArrayBuffer în JavaScript

Date binare (ArrayBuffer, typed arrays) ES2017 (waitAsync: ES2024)

Memorie partajată între fire de execuție (workers) și operații atomice care nu se calcă pe picioare.

Sintaxă
new SharedArrayBuffer(octeti)
Atomics.add(typedArray, indice, valoare)
Atomics.load(typedArray, indice)

Ce face Atomics și SharedArrayBuffer

SharedArrayBuffer este un ArrayBuffer pe care mai mulți workers îl văd în același timp. Când două fire modifică aceeași poziție deodată, o operație obișnuită (a[0]++) poate pierde actualizări. Metodele Atomics fac fiecare operație dintr-o bucată: add, sub, and, or, xor, exchange, compareExchange, load, store.

Atomics.wait oprește firul până când altul apelează Atomics.notify pe aceeași poziție (nu este permis pe firul principal al browserului); waitAsync este varianta cu promisiune. isLockFree spune dacă operațiile pe o anumită mărime sunt implementate direct de procesor, iar pause este o pauză foarte scurtă pentru buclele de așteptare. SharedArrayBuffer are și slice și grow (mărire, dacă a fost creat cu maxByteLength).

În browser, SharedArrayBuffer este disponibil doar pe pagini izolate (anteturile Cross-Origin-Opener-Policy și Cross-Origin-Embedder-Policy).

Parametri

Parametru Descriere
typedArray Un typed array întreg (de obicei Int32Array) creat peste un SharedArrayBuffer.
indice Poziția elementului.
valoare Valoarea folosită în operație.

Ce returnează

Majoritatea operațiilor returnează valoarea veche de pe poziție; load returnează valoarea curentă.

Exemple

Exemplul 1: Operații atomice pe memorie partajată

const memorie = new SharedArrayBuffer(8);
const contoare = new Int32Array(memorie);

console.log(Atomics.add(contoare, 0, 5));
console.log(Atomics.add(contoare, 0, 3));
console.log(Atomics.load(contoare, 0));
console.log(Atomics.compareExchange(contoare, 0, 8, 100), Atomics.load(contoare, 0));
console.log(Atomics.compareExchange(contoare, 0, 8, 0), Atomics.load(contoare, 0));
console.log(Atomics.isLockFree(4));

Rezultat

0
5
8
8 100
100 100
true

Exemplul 2: Un contor incrementat de mai mulți workers (Node.js)

import { Worker } from "node:worker_threads";

const memorie = new SharedArrayBuffer(4);
const cod = `
  const { workerData } = require("node:worker_threads");
  const c = new Int32Array(workerData);
  for (let i = 0; i < 100000; i++) Atomics.add(c, 0, 1);
`;

await Promise.all([1, 2, 3, 4].map(() => new Promise((gata) => {
  new Worker(cod, { eval: true, workerData: memorie }).on("exit", gata);
})));
console.log(Atomics.load(new Int32Array(memorie), 0));

Rezultat

400000

Greșeli frecvente

  • Incrementezi memoria partajată cu a[0]++ din mai multe fire. Unele incrementări se pierd; folosește Atomics.add.
  • Apelezi Atomics.wait pe firul principal al browserului și primești TypeError. Folosește Atomics.waitAsync sau mută așteptarea într-un worker.

Comenzi înrudite

  • ArrayBuffer: Un bloc de memorie brută, de lungime dată în octeți; citit și scris prin typed arrays sau DataView.
  • Typed arrays (Uint8Array, Float64Array...): Tablouri de lungime fixă cu numere de un singur tip, pentru date binare: imagini, fișiere, audio, rețea.
  • Worker (Web Workers): Rulează cod JavaScript pe un fir separat, ca operațiile grele să nu blocheze pagina.