Introduktion till delat minne i JavaScript
Delat minne är en avancerad egenskap av JavaScript, att trådar (samtidigt genomförda delar av en process) kan utnyttja. Delning av minnet betyder inte har problem med att överföra uppdaterade data mellan trådar och alla trådar kan komma åt och uppdatera samma data i det delade minnet.
Låter det inte härligt? Ja, nästan. I det här inlägget ser vi hur man använder delat minne i JavaScript och hur man bestämmer om det här är vad du verkligen vill göra.
Fördelar och nackdelar med delat minne
Vi använder webbarbetare till skapa trådar i JavaScript. Webbarbetar API tillåter oss att skapa arbetstrådar som kan användas till exekvera kod i bakgrunden så att huvudgängan är fri att fortsätta utföra den, eventuellt bearbeta användarhändelser, vilket säkerställer att ingen UI-frysning fryss.
Arbetstrådar köra samtidigt med huvudgängan och varandra. Sådan samtidig utförande av olika delar av en uppgift är tidsbesparande. Du slutar snabbare, men det har också sin egen uppsättning problem.
Se till att varje tråd får de nödvändiga resurserna och kommunicerar med varandra i god tid är en uppgift i sig, där en olycka kan resultera i ett överraskande resultat. Eller om en tråd ändrar data och en annan läser den på samma gång, vad tycker du att den andra tråden kommer att se? Den uppdaterade eller den gamla data?
Webbarbetare är dock inte så lätta att skruva upp. Under deras kommunikation genom att använda meddelanden är de data de skickar varandra inte original men en kopia, vilket betyder att de inte gör det dela med sig samma data. De skicka kopior av data till varandra när det behövs.
Men delning är omtänksam och flera trådar kan också behöva titta på samma data samtidigt och ändra dem. Så, förbjuda delning är ett stort nej-nr. Det är här SharedArrayBuffer
objekt kommer in på bilden. Det kommer att låta oss dela binär data mellan flera trådar.
De SharedArrayBuffer
objekt
I stället för att skicka data kopior mellan trådar, vi skicka kopior av SharedArrayBuffer
objekt. en SharedArrayBuffer
objekt pekar på minnet där data sparas.
Så även när kopiorna av SharedArrayBuffer
passeras mellan trådar, de alla kommer fortfarande att peka på samma minne där originaldata sparas. Gängorna kan således visa och uppdatera data i samma minne.
Webarbetare utan delat minne
För att se hur en webbarbetare arbetar utan att använda delat minne, vi skapa en arbetstråd och skicka några data till den.
De index.html
filen rymmer huvudskript inuti a tagg, som du kan se nedan:
const w = ny arbetare ('worker.js'); var n = 9; w.postMessage (n);
De worker.js
filen bär arbetstagarskript:
onmessage = (e) => console.group ('[worker]'); console.log ('Data mottagna från huvudtråden:% i', e.data); console.groupEnd ();
Med hjälp av koden ovan får vi följande utgång i konsolen:
[arbetare] Uppgifter som mottagits från huvudgänget: 9
Du kan läsa mitt tidigare nämnda inlägg på webbarbetare för fullständig kodförklaring av ovanstående snippets.
För nu, kom ihåg att data är skickas fram och tillbaka mellan trådar använda skicka meddelande()
metod. Uppgifterna är mottaget på andra sidan av meddelande
händelsehanterare, som värdet av händelsen data
fast egendom.
Nu, om vi ändra data kommer det att visas uppdaterat vid mottagningsänden? Låt oss se:
const w = ny arbetare ('worker.js'); var n = 9; w.postMessage (n); n = 1;
Som förväntat data har inte uppdaterats:
[arbetare] Uppgifter som mottagits från huvudgänget: 9
Varför skulle det ändå vara? Dess bara en klon skickad till arbetaren från huvudskriptet.
Webarbetare med delat minne
Nu ska vi Använd SharedArrayBuffer
objekt i samma exempel. Vi kan skapa en ny SharedArrayBuffer
förekomst av använda ny
nyckelord. Konstruktören tar en parameter; en längdvärde i byte, specificerar buffertens storlek.
const w = ny arbetare ('worker.js'); buff = ny SharedArrayBuffer (1); var ar = ny Int8Array (buff); / * inställningsdata * / arr [0] = 9; / * Skicka bufferten (kopia) till arbetstagaren * / w.postMessage (buff);
Observera att a SharedArrayBuffer
objekt representerar endast ett delat minnesområde. Till se och ändra binär data, Vi behöver använda en lämplig datastruktur (a TypedArray
eller a Dataview
objekt).
I index.html
filen ovan, en ny SharedArrayBuffer
skapas, med endast en byte längd. Därefter en ny Int8Array
, vilket är en typ av TypedArray
föremål, är van att ställa in data till “9” i det medföljande byteutrymmet.
onmessage = (e) => var ar = ny Int8Array (e.data); console.group ( '[arbetaren]'); console.log ('Data mottagen från huvudgänget:% i', arr [0]); console.groupEnd ();
Int8Array
används också i arbetaren till visa data i bufferten.
De förväntat värde visas i konsolen från arbetarens tråd, vilket är exakt vad vi ville ha:
[arbetare] Uppgifter som mottagits från huvudgänget: 9
Nu, låt oss uppdatera data i huvudtråden för att se om förändringen återspeglas i arbetstagaren.
const w = ny Arbetare ('worker.js'), buff = ny SharedArrayBuffer (1); var ar = ny Int8Array (buff); / * inställningsdata * / arr [0] = 9; / * Skicka bufferten (kopia) till arbetstagaren * / w.postMessage (buff); / * ändra data * / arr [0] = 1;
Och, som du kan se nedan, uppdateringen reflekterar inuti arbetaren!
[arbetare] Data mottagen från huvudgänget: 1
Men koden också behöver jobba tvärtom: När värdet i arbetstagaren ändras först, det måste också uppdateras när den är tryckt från huvudgängan.
I så fall ser vår kod ut så här:
onmessage = (e) => var ar = ny Int8Array (e.data); console.group ( '[arbetaren]'); console.log ('Data mottagen från huvudgänget:% i', arr [0]); console.groupEnd (); / * ändra data * / arr [0] = 7; / * postar till huvudtråden * / postMessage (");
De Data förändras i arbetstagaren och en tomt meddelande skickas till huvudtråden signalerar att data i bufferten har ändrats och är klar för att huvudgängan ska matas ut.
const w = ny Arbetare ('worker.js'), buff = ny SharedArrayBuffer (1); var ar = ny Int8Array (buff); / * inställningsdata * / arr [0] = 9; / * Skicka bufferten (kopia) till arbetstagaren * / w.postMessage (buff); / * ändra data * / arr [0] = 1; / * skriva ut data efter att arbetaren har ändrat det * / w.onmessage = (e) => console.group ('[main]'); console.log ('Uppdaterad data mottagen från arbetstråd:% i', arr [0]); console.groupEnd ();
Och det fungerar också! Uppgifterna i bufferten är samma som uppgifterna inom arbetaren.
[anställd] Data mottagen från huvudgänget: 1 [huvud] Uppdaterad data mottagen från arbetstråd: 7
Värdet visas uppdaterat i båda fallen; både huvud- och arbetstråd ser och ändrar samma data.
Slutliga ord
Som jag har nämnt tidigare använder jag delat minne i JavaScript är inte utan nackdelar. Det är upp till utvecklare att se till att utföringsföljden händer som förutsagt och inga två trådar tävlar för att få samma data eftersom ingen vet vem som ska ta trofén.
Om du är intresserad av delat minne mer, ta en titt på dokumentationen av Atomics
objekt. De Atomics objekt kan hjälpa dig med några av svårigheterna, genom att minska den oförutsägbara naturen att läsa / skriva från det delade minnet.