Start · Sprachen · JavaScript · Referenz · Atomics.add()

Atomics.add()

Methode

Addiert atomar einen Wert an einer Position eines Integer-TypedArrays und gibt den vorherigen Wert an dieser Position zurück.

seit JavaScript ES2017 Kategorie: method-property

Signatur

Atomics.add(typedArray, index, value)

Beschreibung

Atomics.add() addiert als atomare Operation einen Wert zum Element an einer bestimmten Position eines Integer-TypedArrays. Lesen, Addieren und Zurückschreiben laufen als ein einziger, nicht unterbrechbarer Schritt ab. Kein anderer Thread (Web Worker, Node.js-worker_threads) kann dazwischen auf dieselbe Speicherstelle zugreifen oder sie ändern.

Gebraucht wird das vor allem, wenn mehrere Worker über einen SharedArrayBuffer gemeinsamen Speicher nutzen. Ein normales arr[i] += 1 ist dort eine Race Condition: Zwei Threads lesen womöglich denselben alten Wert, und ein Inkrement geht verloren. Mit Atomics.add() lassen sich zuverlässige Zähler, Fortschrittsanzeigen, Ticket-Vergaben oder Referenzzähler bauen.

Die Methode gibt den Wert vor der Addition zurück (Fetch-and-Add). Das ist hilfreich, um jedem Thread eindeutig eine eigene Nummer oder einen eigenen Index zuzuteilen. Beim Überlauf läuft das Ergebnis entsprechend dem Elementtyp um (Wrap-around). Bei einem Int8Array ergibt 127 + 1 also -128.

Parameter

Name Typ Default Beschreibung
$typedArray Pflicht Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|BigInt64Array|BigUint64Array Ein Integer-TypedArray, typischerweise auf Basis eines SharedArrayBuffer. Float32Array, Float64Array und Uint8ClampedArray sind nicht erlaubt.
$index Pflicht number Position im typedArray, an der addiert wird. Muss innerhalb von 0 bis length - 1 liegen.
$value Pflicht number|bigint Der zu addierende Wert. Bei BigInt64Array und BigUint64Array muss es ein bigint sein (z. B. 5n). Der Wert wird in den Elementtyp umgewandelt.

Rückgabewert

Typ
number|bigint
Beschreibung
Der alte Wert an der Position typedArray[index], also der Wert vor der Addition. Bei BigInt-Arrays wird ein bigint zurückgegeben.

Beispiele

Grundlagen: alten Wert erhalten

const buffer = new SharedArrayBuffer(16);
const counters = new Int32Array(buffer);

counters[0] = 10;

const previous = Atomics.add(counters, 0, 5);
console.log(previous);                   // Wert vor der Addition
console.log(Atomics.load(counters, 0));  // neuer Wert
10 15

Gemeinsamer Zähler mit mehreren Workern (Node.js)

// counter.mjs – mit `node counter.mjs` starten
import { Worker, isMainThread, workerData } from 'node:worker_threads';

if (isMainThread) {
  const shared = new Int32Array(new SharedArrayBuffer(4));
  const workers = Array.from({ length: 4 }, () =>
    new Worker(new URL(import.meta.url), { workerData: shared })
  );

  await Promise.all(workers.map((w) => new Promise((r) => w.on('exit', r))));
  console.log('Gesamt:', Atomics.load(shared, 0));
} else {
  // Jeder Worker erhöht 100.000-mal – ohne verlorene Inkremente
  for (let i = 0; i < 100_000; i++) {
    Atomics.add(workerData, 0, 1);
  }
}
Gesamt: 400000

Eindeutige Ticket-Nummern vergeben

const ticketCounter = new Uint32Array(new SharedArrayBuffer(4));

// Jeder Aufrufer bekommt garantiert eine eigene Nummer,
// auch wenn mehrere Worker gleichzeitig ziehen.
const nextTicket = () => Atomics.add(ticketCounter, 0, 1);

console.log(nextTicket(), nextTicket(), nextTicket());
0 1 2

BigInt-Array und Überlauf

const big = new BigInt64Array(new SharedArrayBuffer(8));
console.log(Atomics.add(big, 0, 42n)); // 0n
console.log(big[0]);                   // 42n

const small = new Int8Array(new SharedArrayBuffer(1));
small[0] = 127;
Atomics.add(small, 0, 1);
console.log(small[0]); // Wrap-around
0n 42n -128

// Wichtig · Fallstricke

  • Fehler: Es wird ein TypeError geworfen, wenn typedArray kein erlaubtes Integer-Array ist, etwa ein Float64Array. Außerhalb der Array-Grenzen liegt, führt zu einem RangeError. Wer eine number an ein BigInt-Array übergibt (oder umgekehrt), bekommt ebenfalls einen TypeError.
  • Rückgabewert: Zurückgegeben wird der alte Wert, nicht das Ergebnis der Addition. Den neuen Wert erhält man mit Atomics.add(a, i, v) + v oder über Atomics.load().
  • Normale ArrayBuffer: Atomics.add() funktioniert auch mit gewöhnlichen ArrayBuffers. Echten Nutzen bringt die Atomarität aber nur bei gemeinsam genutztem Speicher.
  • Browser-Kompatibilität: SharedArrayBuffer steht in modernen Browsern nur in cross-origin-isolierten Kontexten zur Verfügung. Dafür müssen die HTTP-Header Cross-Origin-Opener-Policy: same-origin und Cross-Origin-Embedder-Policy: require-corp gesetzt sein. Das ist eine Schutzmaßnahme gegen Spectre. Prüfen lässt sich die Isolation mit self.crossOriginIsolated. In Node.js gibt es diese Einschränkung nicht.
  • Keine Synchronisation: Atomics.add() macht nur diese eine Operation atomar. Für kritische Abschnitte über mehrere Speicherstellen braucht man zusätzlich Sperren, etwa mit Atomics.compareExchange() und Atomics.wait()/Atomics.notify().