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

Atomics.sub()

Methode

Subtrahiert 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.sub(typedArray, index, value)

Beschreibung

Atomics.sub() zieht einen Wert von dem Element an der angegebenen Position eines Integer-TypedArrays ab. Lesen, Rechnen und Zurückschreiben laufen dabei als eine unteilbare Operation ab. Kein anderer Thread, zum Beispiel ein Web Worker oder ein Node.js-worker_threads-Worker, kann zwischen Lesen und Schreiben auf die Speicherstelle zugreifen. Die Methode gibt den Wert zurück, der vor der Subtraktion dort stand.

Sinnvoll ist die Methode vor allem mit einem SharedArrayBuffer, wenn mehrere Threads einen gemeinsamen Zähler verringern. Typische Fälle sind freie Plätze in einem Pool, verbleibende Aufgaben oder ein Referenzzähler. Ein normales arr[i] -= n ist dort nicht sicher: Zwei Threads können denselben alten Wert lesen, und dann geht eine der beiden Änderungen verloren.

Weil der alte Wert zurückkommt, sieht ein Thread direkt, ob er selbst einen bestimmten Zustand erreicht hat. Liefert Atomics.sub(zaehler, 0, 1) zum Beispiel 1, steht der Zähler jetzt auf 0, und genau dieser Thread hat die letzte Aufgabe erledigt. Diese Auswertung ist frei von Wettlaufsituationen (Race Conditions).

Wird der Wertebereich unterschritten, rechnet die Methode wie bei TypedArrays üblich mit Überlauf modulo 2n. Ein Uint8Array mit dem Wert 0 wird nach einer Subtraktion von 1 also zu 255.

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 Position im typedArray, an der subtrahiert wird.
$value Pflicht number|bigint Der Wert, der abgezogen wird. Bei BigInt64Array und BigUint64Array muss es ein bigint sein.

Rückgabewert

Typ
number|bigint
Beschreibung
Der alte Wert an der Position index, also der Wert vor der Subtraktion. Bei BigInt-Arrays ist es ein bigint.

Beispiele

Grundlegende Subtraktion mit Rückgabe des alten Werts

const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4);
const arr = new Int32Array(sab);
arr[0] = 10;

const alt = Atomics.sub(arr, 0, 3);
console.log(alt);                  // alter Wert
console.log(Atomics.load(arr, 0)); // neuer Wert
10 7

Überlauf bei vorzeichenlosen Arrays

const u8 = new Uint8Array(new SharedArrayBuffer(1));

console.log(Atomics.sub(u8, 0, 1)); // war 0
console.log(u8[0]);                 // unterläuft zu 255
0 255

Offene Aufgaben über mehrere Worker herunterzählen

// main.js
const sab = new SharedArrayBuffer(4);
const offen = new Int32Array(sab);
const anzahlWorker = 4;
Atomics.store(offen, 0, anzahlWorker);

for (let i = 0; i < anzahlWorker; i++) {
  const w = new Worker('worker.js');
  w.postMessage(sab);
  w.onmessage = (e) => console.log(e.data);
}

// worker.js
self.onmessage = ({ data }) => {
  const offen = new Int32Array(data);
  // ... Arbeit erledigen ...
  const vorher = Atomics.sub(offen, 0, 1);
  if (vorher === 1) {
    // Nur genau ein Worker sieht diesen Fall
    self.postMessage('Letzter Worker fertig – alle Aufgaben erledigt');
  }
};
Letzter Worker fertig – alle Aufgaben erledigt

BigInt-Zähler

const big = new BigInt64Array(new SharedArrayBuffer(8));
big[0] = 1_000_000_000_000n;

const alt = Atomics.sub(big, 0, 1n);
console.log(alt, big[0]);
1000000000000n 999999999999n

// Wichtig · Fallstricke

  • Fehler: Ein TypeError entsteht, wenn typedArray kein erlaubter Integer-Typ ist, oder wenn bei einem BigInt-Array eine Zahl statt eines bigint übergeben wird (und umgekehrt). Ein RangeError entsteht, wenn index außerhalb der Array-Grenzen liegt.
  • SharedArrayBuffer im Browser: SharedArrayBuffer gibt es nur in einem cross-origin-isolierten Kontext. Dafür muss der Server die Header Cross-Origin-Opener-Policy: same-origin und Cross-Origin-Embedder-Policy: require-corp senden. Mit self.crossOriginIsolated lässt sich das prüfen. Mit einem normalen ArrayBuffer funktioniert Atomics.sub() zwar auch, bringt dann aber keinen Vorteil.
  • Die Rückgabe ist der alte Wert: Den neuen Wert muss man selbst berechnen (alt - value) oder mit Atomics.load() auslesen. Ein separates load ist aber nicht mehr atomar mit der Subtraktion verbunden.
  • Die Methode subtrahiert immer ohne Bedingung. Soll ein Zähler nicht unter 0 fallen, braucht es eine Schleife mit Atomics.compareExchange().
  • Die Methode ist in allen modernen Browsern und seit Node.js 8.10 verfügbar.