`SharedArrayBuffer` i TypeScript
Denne artikkelen forklarer SharedArrayBuffer i TypeScript.
Vi vil forklare SharedArrayBuffer i TypeScript med praktiske eksempler.
YouTube Video
SharedArrayBuffer i TypeScript
SharedArrayBuffer er en mekanisme for å dele det samme minneområdet mellom flere tråder, slik som Web Workers. Ved å kombinere det med Atomics kan du håndtere dataløp og utføre delte minneoperasjoner med lav latens.
Forutsetninger og merknader
Når du bruker SharedArrayBuffer i nettleseren, kreves COOP- og COEP-headere for å oppfylle sikkerhetskrav kjent som cross-origin isolation. I Node.js kan delt minne håndteres relativt enkelt ved å bruke worker_threads.
Grunnleggende konsepter
SharedArrayBuffer er et objekt som representerer en sekvens med fast lengde av bytes, og du kan lese og skrive tall via TypedArray og lignende visninger. Enkle lese-/skriveoperasjoner er ikke synkronisert, så du bruker Atomics-API for å sikre atomiske operasjoner og bruker wait og notify-mekanismer for koordinering.
Enkel teller (Nettleserversjon)
I dette eksemplet oppretter hovedtråden en SharedArrayBuffer og gir den videre til en Web Worker, hvor begge øker en delt teller. Dette viser det enkleste mønsteret: atomisk addisjon med Atomics.add og lesing med Atomics.load.
main.ts (Nettleserside)
Dette eksemplet viser hvordan hovedtråden oppretter en SharedArrayBuffer og deler den med en Worker for tilgang fra flere tråder.
1// main.ts
2// Create a 4-byte (Int32) shared buffer for one counter
3const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 1);
4const counter = new Int32Array(sab); // view over the buffer
5
6// Create worker and transfer the SharedArrayBuffer
7const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
8worker.postMessage({ sab });
9
10// Increase counter in main thread every 200ms
11setInterval(() => {
12 const newVal = Atomics.add(counter, 0, 1) + 1; // Atomically increment
13 console.log('Main incremented ->', newVal);
14}, 200);
15
16// Listen for messages (optional)
17worker.onmessage = (e) => {
18 console.log('From worker:', e.data);
19};- I denne koden bruker hovedtråden
Atomics.addfor å øke verdien atomisk. Påworker.ts-siden kan den sammeSharedArrayBuffernås og manipuleres.
worker.ts (Nettleser-Worker)
Dette er et eksempel der workeren mottar den samme delte bufferen og periodisk reduserer eller manipulerer den.
1// worker.ts
2self.onmessage = (ev: MessageEvent) => {
3 const { sab } = ev.data as { sab: SharedArrayBuffer };
4 const counter = new Int32Array(sab);
5
6 // Every 350ms, atomically add 2 (demonstration)
7 setInterval(() => {
8 const newVal = Atomics.add(counter, 0, 2) + 2;
9 // Optionally notify main thread (postMessage)
10 self.postMessage(`Worker added 2 -> ${newVal}`);
11 }, 350);
12};- Workeren manipulerer også det samme minnet via
Int32Array, og oppdateringer skjer uten dataløp takket væreAtomics.
Synkronisering med wait/notify
Ved å bruke Atomics.wait og Atomics.notify kan du suspendere tråder til visse betingelser er møtt, noe som muliggjør hendelsesdrevet synkronisering mellom worker-tråder. I nettlesere er det sikreste å bruke Atomics.wait inne i en Worker.
producer.ts (Nettleser Producer Worker)
Produsenten skriver data og varsler konsumenten ved å bruke notify.
1// producer.ts (worker)
2self.onmessage = (ev: MessageEvent) => {
3 const { sab } = ev.data as { sab: SharedArrayBuffer };
4 const state = new Int32Array(sab); // state[0] will be 0=empty,1=filled
5
6 // produce every 500ms
7 setInterval(() => {
8 // produce: set state to 1 (filled)
9 Atomics.store(state, 0, 1);
10 Atomics.notify(state, 0, 1); // wake one waiter
11 self.postMessage('produced');
12 }, 500);
13};consumer.ts (Nettleser Consumer Worker)
Konsumenten venter med Atomics.wait og gjenopptar prosesseringen etter varsling fra produsenten.
1// consumer.ts (worker)
2self.onmessage = (ev: MessageEvent) => {
3 const { sab } = ev.data as { sab: SharedArrayBuffer };
4 const state = new Int32Array(sab);
5
6 // consumer loop
7 (async function consumeLoop() {
8 while (true) {
9 // if state is 0 => wait until notified (value changes)
10 while (Atomics.load(state, 0) === 0) {
11 // Blocks this worker until notified or timeout
12 Atomics.wait(state, 0, 0);
13 }
14 // consume (reset to 0)
15 // (processing simulated)
16 // read data from another shared buffer in real scenarios
17 Atomics.store(state, 0, 0);
18 // continue loop
19 self.postMessage('consumed');
20 }
21 })();
22};- I dette mønsteret varsler produsenten konsumenten via
Atomics.notify, og konsumenten venter effektivt medAtomics.wait.Atomics.waitkan ikke kalles på hovedtråden i nettlesere. For å forhindre at UI fryser, erAtomics.waitbegrenset til bruk kun inne i Workers.
Praktisk eksempel med Node.js (worker_threads)
I et Node.js-miljø kan du håndtere SharedArrayBuffer-funksjonalitet ved å bruke worker_threads. Nedenfor er et typet TypeScript-eksempel for Node.js.
main-node.ts
Hovedtråden oppretter bufferen og gir den videre til Workeren.
1// main-node.ts
2import { Worker } from 'worker_threads';
3import path from 'path';
4
5// Create SharedArrayBuffer for one 32-bit integer
6const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
7const counter = new Int32Array(sab);
8
9// Spawn worker and pass sab via workerData
10const worker = new Worker(path.resolve(__dirname, 'worker-node.js'), {
11 workerData: { sab }
12});
13
14// Log messages from worker
15worker.on('message', (msg) => console.log('Worker:', msg));
16
17// Increment in main thread periodically
18setInterval(() => {
19 const val = Atomics.add(counter, 0, 1) + 1;
20 console.log('Main increment ->', val);
21}, 300);worker-node.ts
Dette eksemplet bruker parentPort og workerData fra worker_threads på Worker-siden.
1// worker-node.ts
2import { parentPort, workerData } from 'worker_threads';
3
4const sab = workerData.sab as SharedArrayBuffer;
5const counter = new Int32Array(sab);
6
7// Periodically add 5
8setInterval(() => {
9 const val = Atomics.add(counter, 0, 5) + 5;
10 parentPort?.postMessage(`Added 5 -> ${val}`);
11}, 700);- I Node.js finnes det ikke COOP- eller COEP-begrensninger som i nettlesere, så delt minne kan håndteres enkelt bare ved å bruke
worker_threads. Når du bruker TypeScript, vær oppmerksom på om du bygger med CommonJS- eller ESM-innstillinger.
TypeScript-typingspunkter
SharedArrayBuffer og Atomics er inkludert i de standard DOM- og biblioteks-typene, så du kan bruke dem direkte i TypeScript. Når du utveksler meldinger med Workers, er det tryggere å definere grensesnitt og spesifisere typer tydelig.
1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };- Eksplicit definering av typer gjør bruk av
postMessageogonmessagetryggere og muliggjør typesjekking.
Praktiske bruksområder
SharedArrayBuffer er ikke alltid nødvendig, men det er svært effektivt i situasjoner hvor hastighetsfordelene med delt minne er påkrevd. Å forstå situasjonene hvor det er effektivt, fører til riktige teknologivalg.
- Den egner seg for behandling med lav latens som krever raske delte buffere, og kan brukes til sanntids lyd-/videobehandling eller spillfysikk.
- For enkel datautveksling eller overføring av store mengder data kan
Transferable ArrayBufferellerpostMessagevære enklere å bruke ennSharedArrayBuffer.
Begrensninger og sikkerhet
For å bruke SharedArrayBuffer i nettleseren kreves cross-origin isolation: sett COOP til same-origin-allow-popups og COEP til require-corp. SharedArrayBuffer vil bli deaktivert med mindre disse kravene er oppfylt.
Ytelses- og feilsøkingstips
Atomiske operasjoner (Atomics) er raske, men hyppig venting og overdreven synkronisering kan øke latenstiden.
Følgende punkter bør sjekkes for å håndtere delt minne sikkert og effektivt.
- Visninger som
Int32Arraymå håndteres med riktig byte-justering. - Vær tydelig på hvilke indekser i den samme delte bufferen som brukes av hvilke prosesser, og oppretthold konsekvente konvensjoner i koden din.
- Hvis du behandler en
SharedArrayBuffersom en vanligArrayBuffer, vil det oppstå dataløp. Å skrive til samme indeks fra flere tråder uten bruk avAtomicser farlig.
Sammendrag
Ved å bruke SharedArrayBuffer og Atomics riktig, kan du oppnå sikre og raske operasjoner med delt minne selv i TypeScript. Å begynne med enkle synkroniseringsmønstre som tellere eller signaler er lettere å forstå, og ved å nøye håndtere korrekt synkronisering og indekser kan du være effektiv selv i scenarier der lav latens er viktig.
Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.