`SharedArrayBuffer` i TypeScript
Den här artikeln förklarar SharedArrayBuffer i TypeScript.
Vi kommer att förklara SharedArrayBuffer i TypeScript med praktiska exempel.
YouTube Video
SharedArrayBuffer i TypeScript
SharedArrayBuffer är en mekanism för att dela samma minnesutrymme mellan flera trådar, såsom Web Workers. Genom att kombinera det med Atomics kan du hantera datakonflikter och utföra shared memory-operationer med låg latens.
Förutsättningar och anmärkningar
När du använder SharedArrayBuffer i webbläsaren krävs COOP- och COEP-headrar för att uppfylla säkerhetskraven som kallas cross-origin isolation. I Node.js kan delat minne hanteras relativt enkelt med hjälp av worker_threads.
Grundläggande begrepp
SharedArrayBuffer är ett objekt som representerar en sekvens av bytes med fast längd, och du kan läsa och skriva siffror via TypedArray och liknande vyer. Enkla läs- och skrivoperationer är inte synkroniserade, så du använder Atomics-API:et för att säkerställa atomära operationer och använder wait- och notify-mekanismer för samordning.
Enkel räknare (webbläsarversion)
I det här exemplet skapar huvudtråden en SharedArrayBuffer och skickar den till en Web Worker, där båda ökar en delad räknare. Detta visar det minsta mönstret: atomär addition med Atomics.add och läsning med Atomics.load.
main.ts (webbläsarsidan)
Detta exempel visar hur huvudtråden skapar en SharedArrayBuffer och delar den med en Worker för tillgång från flera trådar.
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 denna kod använder huvudtråden
Atomics.addför att öka värdet atomärt. Påworker.ts-sidan kan sammaSharedArrayBuffernås och manipuleras.
worker.ts (Webbläsar-Worker)
Detta är ett exempel där workern tar emot samma delade buffert och med jämna mellanrum minskar eller manipulerar 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};- Workern manipulerar också samma minne via
Int32Array, och uppdateringar görs utan datakonflikter tack vareAtomics.
Synkronisering med wait/notify
Genom att använda Atomics.wait och Atomics.notify kan du pausa trådar tills vissa villkor uppfylls, vilket möjliggör händelsedriven synkronisering inom worker-trådar. I webbläsare är det säkrast att använda Atomics.wait inuti en Worker.
producer.ts (Webbläsar-Producer-Worker)
Producenten skriver data och meddelar konsumenten med hjälp av 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 (Webbläsar-Consumer-Worker)
Konsumenten väntar med Atomics.wait och återupptar bearbetningen vid meddelande från 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 detta mönster meddelar producenten konsumenten via
Atomics.notifyoch konsumenten väntar effektivt med hjälp avAtomics.wait.Atomics.waitkan inte anropas på huvudtråden i webbläsare. För att förhindra att användargränssnittet fryser ärAtomics.waitbegränsad till att endast användas inom Workers.
Praktiskt exempel med Node.js (worker_threads)
I en Node.js-miljö kan du hantera SharedArrayBuffer-funktionalitet med hjälp av worker_threads. Nedan finns ett TypeScript-exempel med typer för Node.js.
main-node.ts
Huvudtråden skapar bufferten och skickar den till Workern.
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
Detta exempel använder parentPort och workerData från worker_threads på Worker-sidan.
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 finns det inga COOP- eller COEP-restriktioner som i webbläsare, så delat minne kan enkelt hanteras bara genom att använda
worker_threads. När du använder TypeScript, var uppmärksam på om du bygger med CommonJS- eller ESM-inställningar.
TypeScript-typningspunkter
SharedArrayBuffer och Atomics ingår i de standardiserade DOM- och bibliotekstypdefinitionerna, så du kan använda dem direkt i TypeScript. När du utbyter meddelanden med Workers är det säkrare att definiera gränssnitt och ange typer tydligt.
1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };- Genom att uttryckligen definiera typer blir hanteringen av
postMessageochonmessagesäkrare och möjliggör typkontroll.
Praktiska användningsområden
SharedArrayBuffer är inte alltid nödvändig, men den är mycket effektiv i situationer där hastighetsfördelarna med delat minne behövs. Att förstå i vilka situationer det är effektivt leder till lämpliga teknikval.
- Det är lämpligt för låg-latensbearbetning som kräver snabba delade buffertar och kan användas för realtids ljud-/videobearbetning eller spel-fysik.
- För enkel dataöverföring eller överföring av stora mängder data kan
Transferable ArrayBufferellerpostMessagevara enklare att använda änSharedArrayBuffer.
Begränsningar och säkerhet
För att använda SharedArrayBuffer i webbläsaren krävs cross-origin isolation: sätt COOP till same-origin-allow-popups och COEP till require-corp. SharedArrayBuffer kommer att inaktiveras om dessa krav inte uppfylls.
Prestanda- och felsökningstips
Atomära operationer (Atomics) är snabba, men frekvent väntan och överdriven synkronisering kan öka latensen.
Följande punkter bör kontrolleras för att hantera delat minne säkert och effektivt.
- Vyer som
Int32Arraybör hanteras med korrekt byteuppradning. - Var tydlig med vilka index i samma delade buffert som används av vilka processer, och håll konsekventa konventioner i din kod.
- Om du behandlar en
SharedArrayBuffersom en vanligArrayBufferkommer datakonflikter att uppstå. Att skriva till samma index från flera trådar utan att användaAtomicsär farligt.
Sammanfattning
Genom att använda SharedArrayBuffer och Atomics på rätt sätt kan du uppnå säkra och snabba shared memory-operationer även i TypeScript. Att börja med enkla synkroniseringsmönster som räknare eller signaler är lättare att förstå, och genom att noggrant hantera korrekt synkronisering och index kan du vara effektiv även i scenarier med låg latens.
Du kan följa med i artikeln ovan med hjälp av Visual Studio Code på vår YouTube-kanal. Vänligen kolla även in YouTube-kanalen.