Wprowadzenie do pamięci współdzielonej w JavaScript
Pamięć współdzielona to zaawansowana funkcja JavaScript, która umożliwia wykorzystanie wątków (jednocześnie wykonywanych części procesu). Dzielenie się pamięcią oznacza nie ma problemu z przekazywaniem zaktualizowanych danych między wątkami i wszystkie wątki mogą uzyskać dostęp i aktualizować te same dane w pamięci współdzielonej.
Czy to nie brzmi pięknie? Cóż prawie. W tym poście zobaczymy jak korzystać z pamięci współdzielonej w JavaScript i jak zdecydować, czy naprawdę tego chcesz.
Zalety i wady pamięci współdzielonej
Używamy pracownicy sieci do twórz wątki w JavaScript. Interfejs API Web Workers pozwala nam tworzyć wątki robocze, do których można się przyzwyczaić wykonaj kod w tle aby główny wątek mógł swobodnie kontynuować wykonywanie, ewentualnie przetwarzając zdarzenia interfejsu użytkownika, zapewniając brak zamrożenia interfejsu użytkownika.
Wątki robocze uruchamiać równolegle z głównym wątkiem i sobą nawzajem. Takie jednoczesne wykonywanie różnych części zadania oszczędza czas. Kończysz szybciej, ale ma też swój własny zestaw problemów.
Upewnij się, że każdy wątek uzyskuje niezbędne zasoby i komunikuje się ze sobą w odpowiednim czasie jest zadaniem samym w sobie, w którym nieszczęście może skutkować zaskakującym wynikiem. Albo jeśli jeden wątek zmienia dane, a drugi go czyta w tym samym czasie, jak myślisz, co zobaczy drugi wątek? Zaktualizowane lub stare dane?
Pracownicy sieciowi nie są jednak tak łatwo spieprzyć. Podczas komunikacji za pomocą wiadomości dane, które wysyłają, są nie oryginał, ale kopia, co oznacza, że nie dzielić te same dane. one przekazuj sobie kopie danych kiedy był potrzebny.
Ale dzielenie się jest opieką, a wiele wątków może również potrzebować spojrzeć na te same dane w tym samym czasie i je zmienić. Więc, zakaz dzielenia się jest wielkim nie-nie. To gdzie SharedArrayBuffer
obiekt wchodzi w zdjęcie. Pozwoli nam udostępniaj dane binarne między wieloma wątkami.
The SharedArrayBuffer
obiekt
Zamiast przekazywać kopie danych między wątkami, my przekazać kopie SharedArrayBuffer
obiekt. ZA SharedArrayBuffer
obiekt wskazuje na pamięć, w której dane są zapisywane.
Tak, nawet gdy kopie SharedArrayBuffer
są przekazywane między wątkami, one wszystko nadal będzie wskazywać tę samą pamięć gdzie oryginalne dane są zapisywane. Nici zatem mogą przeglądać i aktualizować dane w tej samej pamięci.
Pracownicy sieci bez pamięć współdzielona
Aby zobaczyć, jak pracownik WWW działa bez użycia pamięci współdzielonej, my utwórz wątek roboczy i przekazać do niego dane.
The index.html
plik zawiera główny skrypt wewnątrz a tag, jak widać poniżej:
const w = new Worker ('worker.js'); var n = 9; w.postMessage (n);
The worker.js
plik niesie skrypt roboczy:
onmessage = (e) => console.group ('[pracownik] ”); console.log ('Dane otrzymane z głównego wątku:% i', e.data); console.groupEnd ();
Korzystając z powyższego kodu, otrzymujemy następujące informacje wyjście w konsoli:
[pracownik] Dane otrzymane z głównego wątku: 9
Możesz przeczytać mój wyżej wymieniony post na temat pracowników sieci w celu wyjaśnienia pełnego kodu powyższych fragmentów.
Na razie pamiętaj, że dane są wysyłane tam i z powrotem między wątkami używając Wyślij wiadomość()
metoda. Dane są otrzymane z drugiej strony przez wiadomość
obsługa zdarzeń, jako wartość wydarzenia dane
własność.
Teraz, jeśli my zmień dane czy pojawi się zaktualizowany na końcu odbioru? Zobaczmy:
const w = new Worker ('worker.js'); var n = 9; w.postMessage (n); n = 1;
Zgodnie z oczekiwaniami dane mają nie został zaktualizowany:
[pracownik] Dane otrzymane z głównego wątku: 9
W każdym razie dlaczego? Jego tylko klon wysłany do pracownika z głównego skryptu.
Pracownicy sieci z pamięć współdzielona
Teraz będziemy Użyj SharedArrayBuffer
obiekt w tym samym przykładzie. Możemy stworzyć nowy SharedArrayBuffer
na przykład przez używając Nowy
słowo kluczowe. Konstruktor przyjmuje jeden parametr; za wartość długości w bajtach, określanie rozmiaru bufora.
const w = new Worker ('worker.js'); buff = new SharedArrayBuffer (1); var arr = new Int8Array (buff); / * dane ustawień * / arr [0] = 9; / * wysyłanie bufora (kopii) do pracownika * / w.postMessage (buff);
Zauważ, że a SharedArrayBuffer
obiekt reprezentuje tylko obszar pamięci współdzielonej. Do zobacz i zmień dane binarne, musimy użyć odpowiedniej struktury danych (a TypedArray
lub a Widok danych
obiekt).
w index.html
plik powyżej, nowy SharedArrayBuffer
jest tworzony z tylko jednobajtową długością. Potem nowy Int8Array
, który jest jednym z typów TypedArray
przedmioty, do których jest przyzwyczajony ustaw dane na “9” w podanej przestrzeni bajtów.
onmessage = (e) => var arr = new Int8Array (e.data); console.group („[pracownik]”); console.log ('Dane otrzymane z głównego wątku:% i', arr [0]); console.groupEnd ();
Int8Array
jest również używany u pracownika, do wyświetl dane w buforze.
The oczekiwana wartość pojawi się w konsoli z wątku robotniczego, który jest dokładnie tym, czego chcieliśmy:
[pracownik] Dane otrzymane z głównego wątku: 9
A teraz zaktualizuj dane w głównym wątku sprawdzić, czy zmiana jest odzwierciedlona w pracowniku.
const w = new Worker ('worker.js'), buff = new SharedArrayBuffer (1); var arr = new Int8Array (buff); / * dane ustawień * / arr [0] = 9; / * wysyłanie bufora (kopii) do pracownika * / w.postMessage (buff); / * zmiana danych * / arr [0] = 1;
I, jak widać poniżej, aktualizacja odbija się wewnątrz pracownika!
[pracownik] Dane otrzymane z głównego wątku: 1
Ale także kod musi działać na odwrót: kiedy wartość w pracowniku zmienia się na początku, to również wymaga aktualizacji kiedy jest drukowany z głównego wątku.
W tym przypadku nasz kod wygląda tak:
onmessage = (e) => var arr = new Int8Array (e.data); console.group („[pracownik]”); console.log ('Dane otrzymane z głównego wątku:% i', arr [0]); console.groupEnd (); / * zmiana danych * / arr [0] = 7; / * wysyłanie do głównego wątku * / postMessage (");
The dane są zmieniane u pracownika i an pusta wiadomość jest wysyłana do głównego wątku sygnalizacja, że dane w buforze zostały zmienione i jest gotowe do wyprowadzenia głównego wątku.
const w = new Worker ('worker.js'), buff = new SharedArrayBuffer (1); var arr = new Int8Array (buff); / * dane ustawień * / arr [0] = 9; / * wysyłanie bufora (kopii) do pracownika * / w.postMessage (buff); / * zmiana danych * / arr [0] = 1; / * drukowanie danych po zmianie przez pracownika * / w.onmessage = (e) => console.group ('[main]'); console.log („Zaktualizowane dane otrzymane od wątku roboczego:% i”, arr [0]); console.groupEnd ();
I to też działa! Dane w buforze są takie same jak dane wewnątrz pracownika.
[worker] Dane otrzymane z głównego wątku: 1 [main] Zaktualizowane dane otrzymane od wątku roboczego: 7
Wartość pojawia się zaktualizowany w obu przypadkach; zarówno główny, jak i roboczy wątek przeglądają i zmieniają te same dane.
Ostatnie słowa
Jak już wspomniałem wcześniej, korzystanie z pamięci współdzielonej w JavaScript nie jest bez wad. Od programistów zależy, czy sekwencja wykonania odbywa się zgodnie z przewidywaniami i żadne dwa wątki nie ścigają się, aby uzyskać te same dane, ponieważ nikt nie wie, kto zajmie trofeum.
Jeśli jesteś bardziej zainteresowany pamięcią współdzieloną, zajrzyj do dokumentacji Atomika
obiekt. The Obiekt Atomics może pomóc w niektórych trudach, przez zmniejszenie nieprzewidywalnej natury odczytu / zapisu z pamięci współdzielonej.