Signatur
Beschreibung
Atomics.exchange() ist eine statische Methode des Atomics-Namensraums. Sie liest den aktuellen Wert an index, ersetzt ihn durch value und gibt den alten Wert zurück. Das alles geschieht als eine einzige, unteilbare Operation. Kein anderer Thread (Worker) kann zwischen Lesen und Schreiben auf die Speicherstelle zugreifen.
Die Methode ist vor allem bei Speicher sinnvoll, den sich mehrere Worker über einen SharedArrayBuffer teilen. Typische Einsatzfälle sind einfache Spinlocks, das Übernehmen eines Flags („Ich hole mir den Job und setze den Status gleichzeitig auf belegt“) oder das Abholen und Zurücksetzen eines Zählers bzw. Puffers in einem Schritt.
Im Unterschied zu Atomics.compareExchange() schreibt exchange() immer, ganz gleich, welcher Wert vorher dort stand. Wenn nur unter einer Bedingung geschrieben werden soll, ist compareExchange() die richtige Wahl. Der Rückgabewert von exchange() zeigt an, welcher Zustand vorher bestand, und macht damit Lock-Logik möglich.
Die Methode funktioniert auch mit einem normalen ArrayBuffer. Dort ist sie aber nur dann sinnvoll, wenn Code sowohl mit geteiltem als auch mit ungeteiltem Speicher laufen soll.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $typedArray Pflicht | Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|BigInt64Array|BigUint64Array | Ein Integer-TypedArray, meist über einem SharedArrayBuffer. Float32Array, Float64Array und Uint8ClampedArray sind nicht erlaubt. |
|
| $index Pflicht | number | Die Position im Array, an der der Wert ausgetauscht wird. Liegt sie außerhalb des Bereichs, wird ein RangeError ausgelöst. |
|
| $value Pflicht | number|bigint | Der neue Wert. Er wird in den Elementtyp des Arrays umgewandelt (z. B. mit Überlauf bei Uint8Array). Bei BigInt64Array/BigUint64Array muss ein bigint übergeben werden. |
Rückgabewert
index stand. Bei BigInt-Arrays ist das ein bigint.Beispiele
Grundlegender Austausch
const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4);
const ta = new Int32Array(sab);
ta[0] = 7;
const alt = Atomics.exchange(ta, 0, 12);
console.log(alt); // alter Wert
console.log(ta[0]); // neuer Wert
Einfacher Spinlock zwischen Workern
// lock.js – wird im Haupt-Thread und in den Workern mit demselben SharedArrayBuffer genutzt
const UNLOCKED = 0;
const LOCKED = 1;
const acquire = (lock) => {
// exchange liefert UNLOCKED nur für den Thread, der das Lock tatsächlich bekommt
while (Atomics.exchange(lock, 0, LOCKED) !== UNLOCKED) {
Atomics.wait(lock, 0, LOCKED); // blockiert (nur in Workern erlaubt)
}
};
const release = (lock) => {
Atomics.store(lock, 0, UNLOCKED);
Atomics.notify(lock, 0, 1); // einen wartenden Worker wecken
};
// Nutzung im Worker:
// const lock = new Int32Array(sharedBuffer, 0, 1);
// acquire(lock); try { /* kritischer Abschnitt */ } finally { release(lock); }
Zähler auslesen und zurücksetzen
const stats = new BigInt64Array(new SharedArrayBuffer(8));
Atomics.add(stats, 0, 42n); // Worker zählen Anfragen hoch
// Periodisch: Zählerstand abholen und in einem Schritt auf 0 setzen
const anfragen = Atomics.exchange(stats, 0, 0n);
console.log(`Anfragen seit letztem Report: ${anfragen}`);
console.log(stats[0]);
Umwandlung in den Elementtyp
const u8 = new Uint8Array(new SharedArrayBuffer(1));
console.log(Atomics.exchange(u8, 0, 300)); // alter Wert
console.log(u8[0]); // 300 mod 256
// Wichtig · Fallstricke
- Typfehler: Bei Float-Arrays,
Uint8ClampedArrayoder Nicht-TypedArrays wird einTypeErrorausgelöst, bei einem Index außerhalb des Bereichs einRangeError. Wer einer BigInt-Array-Position einenumberübergibt, erhält ebenfalls einenTypeError. - SharedArrayBuffer ist in Browsern eingeschränkt: Seit Spectre steht
SharedArrayBuffernur in cross-origin-isolierten Kontexten zur Verfügung. Dafür müssen die HeaderCross-Origin-Opener-Policy: same-originundCross-Origin-Embedder-Policy: require-corpgesetzt sein. Ob das zutrifft, lässt sich mitself.crossOriginIsolatedprüfen. - Nicht mit normalem Schreiben mischen: Auf dieselbe Speicherstelle sollten alle Threads nur über
Atomics-Methoden zugreifen. Einfache Zuweisungen wieta[0] = xsind nicht synchronisiert. - Spinlocks: Eine Schleife mit
exchange()ohneAtomics.wait()verbraucht CPU-Zeit. Im Haupt-Thread des Browsers istAtomics.wait()nicht erlaubt. Dort bietet sichAtomics.waitAsync()an. - Die Methode wird von allen modernen Browsern unterstützt (Baseline), außerdem von Node.js ab Version 8.10. BigInt-Arrays funktionieren ab ES2020.