`SharedArrayBuffer` i TypeScript
Denne artikel forklarer SharedArrayBuffer i TypeScript.
Vi vil forklare SharedArrayBuffer i TypeScript med praktiske eksempler.
YouTube Video
SharedArrayBuffer i TypeScript
SharedArrayBuffer er en mekanisme til at dele den samme hukommelse mellem flere tråde, såsom Web Workers. Ved at kombinere det med Atomics kan du håndtere datakapløb og udføre hurtige delte hukommelsesoperationer med lav latenstid.
Forudsætninger og Bemærkninger
Når du bruger SharedArrayBuffer i browseren, kræves COOP- og COEP-headere for at opfylde sikkerhedskravene kendt som cross-origin isolation. I Node.js kan delt hukommelse håndteres forholdsvis nemt ved hjælp af worker_threads.
Grundlæggende Koncepter
SharedArrayBuffer er et objekt, der repræsenterer en sekvens af bytes med fast længde, og du kan læse og skrive tal via TypedArray og lignende views. Enkle læse/skriv-operationer er ikke synkroniseret, så du bruger Atomics API'et til at sikre atomare operationer og bruger wait og notify mekanismer til koordinering.
Simpel Tæller (Browserversion)
I dette eksempel opretter hovedtråden en SharedArrayBuffer og giver den videre til en Web Worker, hvor begge øger en delt tæller. Dette demonstrerer det minimale mønster: atomar addition med Atomics.add og læsning med Atomics.load.
main.ts (Browser-side)
Dette eksempel viser, hvordan hovedtråden opretter en SharedArrayBuffer og deler den med en Worker for adgang med flere tråde.
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 kode bruger hovedtråden
Atomics.addtil at øge værdien atomart. Påworker.ts-siden kan den sammeSharedArrayBuffertilgås og manipuleres.
worker.ts (Browser Worker)
Dette er et eksempel, hvor worker'en modtager den samme delte buffer og periodisk formindsker 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};- Worker'en manipulerer også den samme hukommelse via
Int32Array, og opdateringer foretages uden datakapløb takket væreAtomics.
Synkronisering med wait/notify
Ved at bruge Atomics.wait og Atomics.notify kan du suspendere tråde indtil visse betingelser er opfyldt, hvilket muliggør hændelsesbaseret synkronisering i worker-tråde. I browsere er det sikreste at bruge Atomics.wait inde i en Worker.
producer.ts (Browser Producer Worker)
Producenten skriver data og underretter forbrugeren ved hjælp af 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 (Browser Consumer Worker)
Forbrugeren venter med Atomics.wait og genoptager behandlingen ved underretning fra producenten.
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ønster underretter producenten forbrugeren via
Atomics.notify, og forbrugeren venter effektivt medAtomics.wait.Atomics.waitkan ikke kaldes på hovedtråden i browsere. For at undgå at brugerfladen fryser, er brugen afAtomics.waitbegrænset til kun at blive brugt i Workers.
Praktisk Eksempel med Node.js (worker_threads)
I et Node.js-miljø kan du håndtere SharedArrayBuffer-funktionalitet ved hjælp af worker_threads. Nedenfor er et typed TypeScript-eksempel til Node.js.
main-node.ts
Hovedtråden opretter bufferen og viderebringer den til Worker.
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 eksempel bruger 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 er der ingen COOP- eller COEP-begrænsninger som i browsere, så delt hukommelse kan nemt håndteres blot ved at anvende
worker_threads. Når du bruger TypeScript, skal du være opmærksom på, om du bygger med CommonJS- eller ESM-indstillinger.
TypeScript Typings Tips
SharedArrayBuffer og Atomics er inkluderet i de standard DOM- og biblioteks-typede definitioner, så du kan bruge dem direkte i TypeScript. Når du udveksler beskeder med Workers, er det sikrest at definere interfaces og tydeligt angive typer.
1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };- Eksplicit definition af typer gør håndtering af
postMessageogonmessagemere sikker og muliggør typekontrol.
Praktiske Anvendelser
SharedArrayBuffer er ikke altid nødvendig, men den er meget effektiv i situationer, hvor der kræves hastighedsfordele ved delt hukommelse. Forståelse af de situationer, hvor det er effektivt, fører til passende teknologivalg.
- Det er velegnet til behandlinger med lav latenstid, der kræver højhastigheds delte buffere, og kan benyttes til realtids lyd-/videobehandling eller fysik i spil.
- Til simpel dataudveksling eller overførsel af store datamængder kan
Transferable ArrayBufferellerpostMessagevære lettere at bruge endSharedArrayBuffer.
Begrænsninger og Sikkerhed
For at bruge SharedArrayBuffer i browseren kræves cross-origin isolation: sæt COOP til same-origin-allow-popups og COEP til require-corp. SharedArrayBuffer vil blive deaktiveret, medmindre disse krav er opfyldt.
Ydeevne og Fejlfindingsråd
Atomare operationer (Atomics) er hurtige, men hyppig venten og overdreven synkronisering kan øge latenstiden.
Følgende punkter bør kontrolleres for at håndtere delt hukommelse sikkert og effektivt.
- Views som
Int32Arraybør håndteres med korrekt bytejustering. - Vær tydelig om hvilke indekser af den samme delte buffer, der bruges af hvilke processer, og oprethold konsistente konventioner i din kode.
- Hvis du behandler en
SharedArrayBuffersom en almindeligArrayBuffer, vil der opstå datakapløb. At skrive til samme indeks fra flere tråde uden brug afAtomicser farligt.
Sammendrag
Ved korrekt brug af SharedArrayBuffer og Atomics kan du opnå sikre og hurtige delte hukommelsesoperationer, selv i TypeScript. Det er lettere at forstå at begynde med simple synkroniseringsmønstre som tællere eller signaler, og ved omhyggelig håndtering af korrekt synkronisering og indekser kan du være effektiv, selv i situationer med lave latenstider.
Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.