SharedArrayBuffer i JavaScript

SharedArrayBuffer i JavaScript

Den här artikeln förklarar SharedArrayBuffer i JavaScript.

Vi kommer att ge en detaljerad förklaring av grunderna för SharedArrayBuffer, hur man använder det, specifika användningsområden och säkerhetsöverväganden.

YouTube Video

SharedArrayBuffer i JavaScript

SharedArrayBuffer är ett kraftfullt verktyg i JavaScript för att dela minne mellan flera trådar. Särskilt i kombination med Web Workers möjliggör det parallellbearbetning, vilket gör det effektivt för beräkningsintensiva uppgifter och applikationer som kräver realtidsfunktioner.

Vad är SharedArrayBuffer?

SharedArrayBuffer tillhandahåller en minnesbuffert i JavaScript som möjliggör delning av binära data mellan flera trådar (främst Web Workers). En vanlig ArrayBuffer kräver kopiering mellan huvudtråden och arbetarna, men SharedArrayBuffer tillåter direkt minnesdelning utan kopiering, vilket avsevärt förbättrar prestandan.

Funktioner

  • Delat minne Det möjliggör för flera trådar att arbeta med samma minnesutrymme.
  • Prestandaförbättring Eftersom kopiering kan utelämnas minskas overheaden vid bearbetning av stora datamängder.
  • Trådsynkronisering Du kan använda det tillsammans med Atomics för att genomföra synkronisering och undvika konflikter vid minnesåtkomst.

Grundläggande användningsexempel

 1// Create a 16-byte shared memory
 2const sharedBuffer = new SharedArrayBuffer(16);
 3
 4// Treat it as an Int32Array
 5const sharedArray = new Int32Array(sharedBuffer);
 6
 7// Set a value
 8sharedArray[0] = 42;
 9
10console.log(sharedArray[0]);  // 42

I det här exemplet skapar vi ett 16-byte stort minnesområde med SharedArrayBuffer och behandlar det minnesområdet som en Int32Array. Denna minnesbuffert kan delas mellan flera trådar.

Användning med Web Workers

Det verkliga värdet av SharedArrayBuffer visas när det används tillsammans med Web Workers. Följande kod är ett exempel på hur man använder delat minne mellan huvudtråden och en arbetstråd.

På huvudtråden

 1// Create a shared buffer
 2const sharedBuffer = new SharedArrayBuffer(16);
 3const sharedArray = new Int32Array(sharedBuffer);
 4
 5// Create a worker
 6const worker = new Worker('worker.js');
 7
 8// Pass the shared buffer to the worker
 9worker.postMessage(sharedBuffer);
10
11// Modify the memory
12// Output : Main thread: 100
13sharedArray[0] = 100;
14console.log("Main thread: ", sharedArray[0]);

På arbetarsidan (worker.js)

 1// worker.js
 2self.onmessage = function(event) {
 3    // Use the received shared buffer
 4    const sharedArray = new Int32Array(event.data);
 5
 6    // Read the contents of the memory
 7    // Output : Worker thread: 100
 8    console.log("Worker thread: ", sharedArray[0]);
 9
10    // Change the value
11    sharedArray[0] = 200;
12};
  • I det här exemplet skapar huvudtråden en delad buffert och skickar den till arbetaren. Arbetaren kan få åtkomst till denna buffert för att läsa och ändra värden. På detta sätt kan data delas mellan trådar utan kopiering.

Dubbelriktad uppdateringsbekräftelse

Genom att använda SharedArrayBuffer kan både huvudtråden och arbetstrådar läsa och skriva till samma minne, vilket möjliggör dubbelriktad uppdateringsbekräftelse. Nedan är ett exempel där huvudtråden sätter ett värde, en arbetstråd ändrar värdet och sedan kontrollerar huvudtråden uppdateringen.

På huvudtråden

 1// Create a shared buffer
 2const sharedBuffer = new SharedArrayBuffer(16);
 3const sharedArray = new Int32Array(sharedBuffer);
 4
 5// Create a worker
 6const worker = new Worker('worker.js');
 7
 8// Pass the shared buffer to the worker
 9worker.postMessage(sharedBuffer);
10
11// Set initial value
12// Output : Main thread initial: 100
13sharedArray[0] = 100;
14console.log("Main thread initial:", sharedArray[0]);
15
16// Listen for worker confirmation
17worker.onmessage = () => {
18    // Output : Main thread after worker update: 200
19    console.log("Main thread after worker update:", sharedArray[0]);
20};

På arbetarsidan (worker.js)

 1// worker.js
 2self.onmessage = function(event) {
 3    const sharedArray = new Int32Array(event.data);
 4
 5    // Read initial value
 6    // Output : Worker thread received: 100
 7    console.log("Worker thread received:", sharedArray[0]);
 8
 9    // Update the value
10    sharedArray[0] = 200;
11
12    // Notify main thread
13    self.postMessage("Value updated");
14};
  • I det här exemplet skriver huvudtråden först värdet 100, och efter att arbetstråden har läst det skriver den om det till 200. Därefter meddelar arbetstråden huvudtråden, och huvudtråden läser åter det delade minnet för att bekräfta uppdateringen. På detta sätt möjliggör kombinationen av aviseringar dubbelriktad uppdateringsbekräftelse.

Synkronisering med Atomics

Vid användning av delat minne måste man vara försiktig med datarace och inkonsekvenser. När flera trådar åtkomst till samma minne samtidigt kan konflikter uppstå. För att förhindra detta använder JavaScript objektet Atomics för synkronisering.

Till exempel, för att säkert öka en räknare med flera trådar kan du använda Atomics för att förhindra konflikter.

1const sharedBuffer = new SharedArrayBuffer(16);
2const sharedArray = new Int32Array(sharedBuffer);
3
4// Increment the counter
5Atomics.add(sharedArray, 0, 1);
6
7console.log(Atomics.load(sharedArray, 0));  // 1

Atomics.add ökar värdet på en specifik index atomärt och returnerar det nya värdet. Denna operation är garanterat konfliktfri med andra trådar. Atomics.load används också för att säkert läsa värden från delat minne.

Väntan och avisering med Atomics.wait och Atomics.notify

Vid användning av SharedArrayBuffer kan det uppstå situationer där en arbetstråd behöver vänta tills ett visst villkor är uppfyllt, och när en annan tråd uppfyller detta villkor måste den meddela den väntande tråden. I sådana fall är Atomics.wait och Atomics.notify användbara.

Atomics.wait blockerar en tråd tills värdet på en specifik plats i det delade minnet ändras, medan Atomics.notify meddelar väntande trådar att de kan fortsätta. Detta möjliggör säker väntan och avisering mellan flera arbetstrådar. Dock kan Atomics.wait inte användas på huvudtråden och är endast tillgängligt inuti workers.

 1// Create a shared buffer (1 Int32 slot is enough for signaling)
 2const sharedBuffer = new SharedArrayBuffer(4);
 3const sharedArray = new Int32Array(sharedBuffer);
 4
 5// Create workers with names
 6const waiter = new Worker('worker.js', { name: 'waiter' });
 7const notifier = new Worker('worker.js', { name: 'notifier' });
 8
 9// Pass the shared buffer to both
10waiter.postMessage(sharedBuffer);
11notifier.postMessage(sharedBuffer);
12
13// Listen for messages
14waiter.onmessage = (event) => {
15    console.log(`[Main] Message from waiter:`, event.data);
16};
17notifier.onmessage = (event) => {
18    console.log(`[Main] Message from notifier:`, event.data);
19};
  • På huvudtråden skapas en SharedArrayBuffer som delat minne och omvandlas till en Int32Array med endast ett element. Denna enda heltalsplats används som en signal för att synkronisera mellan workers. Nästa steg är att två workers skapas, och var och en tilldelas en roll med name-egenskapen: waiter (väntande roll) och notifier (notifierande roll). Slutligen skickas den delade bufferten till båda workers, och onmessage-hanterare ställs in så att meddelanden som skickas från varje worker kan tas emot.

På arbetarsidan (worker.js)

 1// worker.js
 2onmessage = (event) => {
 3    const sharedArray = new Int32Array(event.data);
 4
 5    if (self.name === 'waiter') {
 6        postMessage('Waiter is waiting...');
 7        // Wait until notifier signals index 0
 8        Atomics.wait(sharedArray, 0, 0);
 9        postMessage('Waiter was notified!');
10    }
11
12    if (self.name === 'notifier') {
13        postMessage('Notifier is preparing...');
14        setTimeout(() => {
15            // Notify waiter after 2 seconds
16            Atomics.store(sharedArray, 0, 1);
17            Atomics.notify(sharedArray, 0, 1);
18            postMessage('Notifier has sent the signal!');
19        }, 2000);
20    }
21};
22// Output
23// [Main] Message from waiter: Waiter is waiting...
24// [Main] Message from notifier: Notifier is preparing...
25// [Main] Message from notifier: Notifier has sent the signal!
26// [Main] Message from waiter: Waiter was notified!
  • I detta exempel stannar waiter-workern i väntetillstånd med hjälp av Atomics.wait så länge värdet på index 0 är 0. Å andra sidan, när notifier-workern ändrar värdet till 123 med Atomics.store och anropar Atomics.notify, kommer waiter-workern att återupptas och kunna hämta det uppdaterade värdet. Med detta kan effektiv och säker väntan och notifiering mellan trådar uppnås.

Användningsområden för SharedArrayBuffer

SharedArrayBuffer är särskilt användbar för följande användningsområden:.

  • Realtidsbehandling Det är lämpligt för applikationer som kräver låg latens, såsom ljud- och videobearbetning eller spelmotorer, där data omedelbart behöver delas mellan trådar.
  • Parallellberäkning Vid samtidig bearbetning av stora datamängder med flera trådar undviker användningen av SharedArrayBuffer minneskopiering och kan förbättra prestandan.
  • Maskininlärning Genom att parallelisera uppgifter som databehandling och inferens blir effektiv beräkning möjlig.

Säkerhetsöverväganden

SharedArrayBuffer är en kraftfull funktion men medför också säkerhetsrisker. Särskilt oro för sidokanalsattacker som Spectre har tillfälligt stoppat dess stöd. För att mildra denna sårbarhet har webbläsare implementerat följande åtgärder:.

  • Webbplatsisolering Webbplatser som tillåter användning av SharedArrayBuffer kommer att köras i en process som är helt isolerad från andra webbplatser.
  • Cross-Origin Resource Policy För att använda SharedArrayBuffer måste rubrikerna Cross-Origin-Opener-Policy och Cross-Origin-Embedder-Policy vara korrekt inställda.

Till exempel genom att ställa in headers som följande blir användningen av SharedArrayBuffer möjlig:.

1Cross-Origin-Opener-Policy: same-origin
2Cross-Origin-Embedder-Policy: require-corp

Detta förhindrar externa resurser från att störa det aktuella innehållet och ökar säkerheten.

Sammanfattning

SharedArrayBuffer är ett mycket kraftfullt verktyg för att dela minne mellan flera trådar. Det är en viktig teknologi för att förbättra prestandan, och dess effekter är särskilt tydliga inom områdena realtidsbehandling och parallell beräkning. Dock innebär det också säkerhetsrisker, så korrekt konfiguration och synkronisering är viktiga.

Genom att använda SharedArrayBuffer kan du bygga mer avancerade och högpresterande webbapplikationer.

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.

YouTube Video