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

Atomics.exchange()

Methode

Schreibt atomar einen neuen Wert an eine Position eines Integer-TypedArrays und gibt den dort zuvor gespeicherten Wert zurück.

seit JavaScript ES2017 Kategorie: method-property

Signatur

Atomics.exchange(typedArray, index, value)

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

Typ
number|bigint
Beschreibung
Der alte Wert, der vor dem Austausch an 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
7 12

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]);
Anfragen seit letztem Report: 42 0n

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
0 44

// Wichtig · Fallstricke

  • Typfehler: Bei Float-Arrays, Uint8ClampedArray oder Nicht-TypedArrays wird ein TypeError ausgelöst, bei einem Index außerhalb des Bereichs ein RangeError. Wer einer BigInt-Array-Position eine number übergibt, erhält ebenfalls einen TypeError.
  • SharedArrayBuffer ist in Browsern eingeschränkt: Seit Spectre steht SharedArrayBuffer nur in cross-origin-isolierten Kontexten zur Verfügung. Dafür müssen die Header Cross-Origin-Opener-Policy: same-origin und Cross-Origin-Embedder-Policy: require-corp gesetzt sein. Ob das zutrifft, lässt sich mit self.crossOriginIsolated prüfen.
  • Nicht mit normalem Schreiben mischen: Auf dieselbe Speicherstelle sollten alle Threads nur über Atomics-Methoden zugreifen. Einfache Zuweisungen wie ta[0] = x sind nicht synchronisiert.
  • Spinlocks: Eine Schleife mit exchange() ohne Atomics.wait() verbraucht CPU-Zeit. Im Haupt-Thread des Browsers ist Atomics.wait() nicht erlaubt. Dort bietet sich Atomics.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.