Signatur
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
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
Ü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
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');
}
};
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]);
// Wichtig · Fallstricke
- Fehler: Ein
TypeErrorentsteht, wenntypedArraykein erlaubter Integer-Typ ist, oder wenn bei einem BigInt-Array eine Zahl statt einesbigintübergeben wird (und umgekehrt). EinRangeErrorentsteht, wennindexaußerhalb der Array-Grenzen liegt. - SharedArrayBuffer im Browser:
SharedArrayBuffergibt es nur in einem cross-origin-isolierten Kontext. Dafür muss der Server die HeaderCross-Origin-Opener-Policy: same-originundCross-Origin-Embedder-Policy: require-corpsenden. Mitself.crossOriginIsolatedlässt sich das prüfen. Mit einem normalenArrayBufferfunktioniertAtomics.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 mitAtomics.load()auslesen. Ein separatesloadist 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.