Signatur
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
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
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);
}
}
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());
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
// Wichtig · Fallstricke
- Fehler: Es wird ein
TypeErrorgeworfen, wenntypedArraykein erlaubtes Integer-Array ist, etwa einFloat64Array. Außerhalb der Array-Grenzen liegt, führt zu einemRangeError. Wer einenumberan ein BigInt-Array übergibt (oder umgekehrt), bekommt ebenfalls einenTypeError. - 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) + voder überAtomics.load(). - Normale ArrayBuffer:
Atomics.add()funktioniert auch mit gewöhnlichenArrayBuffers. Echten Nutzen bringt die Atomarität aber nur bei gemeinsam genutztem Speicher. - Browser-Kompatibilität:
SharedArrayBuffersteht in modernen Browsern nur in cross-origin-isolierten Kontexten zur Verfügung. Dafür müssen die HTTP-HeaderCross-Origin-Opener-Policy: same-originundCross-Origin-Embedder-Policy: require-corpgesetzt sein. Das ist eine Schutzmaßnahme gegen Spectre. Prüfen lässt sich die Isolation mitself.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 mitAtomics.compareExchange()undAtomics.wait()/Atomics.notify().