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 till200
. 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 enInt32Array
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 medname
-egenskapen:waiter
(väntande roll) ochnotifier
(notifierande roll). Slutligen skickas den delade bufferten till båda workers, ochonmessage
-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 avAtomics.wait
så länge värdet på index0
är0
. Å andra sidan, närnotifier
-workern ändrar värdet till123
medAtomics.store
och anroparAtomics.notify
, kommerwaiter
-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 rubrikernaCross-Origin-Opener-Policy
ochCross-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.