Węzeł ma zupełnie inny paradygmat i po prawidłowym ujęciu łatwiej jest dostrzec inny sposób rozwiązywania problemów. Nigdy nie potrzebujesz wielu wątków w aplikacji Node (1), ponieważ masz inny sposób robienia tego samego. Tworzysz wiele procesów; ale bardzo różni się od tego, na przykład jak działa Prefork mpm Apache Web Server.
Na razie pomyślmy, że mamy tylko jeden rdzeń procesora i opracujemy aplikację (na sposób Node) do wykonania jakiejś pracy. Naszym zadaniem jest przetworzenie dużego pliku zawierającego bajt po bajcie. Najlepszym sposobem dla naszego oprogramowania jest rozpoczęcie pracy od początku pliku i śledzenie go bajt po bajcie do końca.
- Hej, Hasan, przypuszczam, że jesteś nowicjuszem lub bardzo starą szkołą z czasów mojego dziadka !!! Dlaczego nie utworzysz kilku wątków i nie przyspieszysz tego?
- Och, mamy tylko jeden rdzeń procesora.
-- Więc co? Stwórz jakieś wątki człowieku, zrób to szybciej!
- To tak nie działa. Jeśli utworzę wątki, spowalniam je. Ponieważ będę dodawał dużo narzutów do systemu, aby przełączać się między wątkami, próbując dać im odpowiednią ilość czasu, a także wewnątrz mojego procesu, próbując komunikować się między tymi wątkami. Oprócz tych wszystkich faktów będę musiał również pomyśleć o tym, jak podzielę jedną pracę na wiele części, które można wykonać równolegle.
- Dobra, dobra, widzę, że jesteś biedny. Skorzystajmy z mojego komputera, który ma 32 rdzenie!
- Wow, jesteś niesamowity mój drogi przyjacielu, bardzo dziękuję. Doceniam to!
Następnie wracamy do pracy. Dzięki naszemu bogatemu przyjacielowi mamy teraz 32 rdzenie procesora. Zasady, których musimy przestrzegać, właśnie się zmieniły. Teraz chcemy spożytkować całe otrzymane bogactwo.
Aby korzystać z wielu rdzeni, musimy znaleźć sposób na podzielenie naszej pracy na części, które możemy obsługiwać równolegle. Gdyby to nie był Node, użylibyśmy do tego wątków; 32 wątki, po jednym na każdy rdzeń procesora. Jednak ponieważ mamy Node, stworzymy 32 procesy Node.
Wątki mogą być dobrą alternatywą dla procesów Node, a może nawet lepszym sposobem; ale tylko na konkretnym stanowisku, gdzie praca jest już zdefiniowana i mamy pełną kontrolę nad tym, jak ją wykonać. Poza tym w przypadku każdego innego rodzaju problemu, w którym praca pochodzi z zewnątrz w sposób, nad którym nie mamy kontroli i chcemy odpowiedzieć tak szybko, jak to możliwe, droga Node jest bezspornie lepsza.
- Hej, Hasan, nadal pracujesz jednowątkowo? Co jest z tobą nie tak, stary? Właśnie zapewniłem ci to, czego chciałeś. Nie masz już wymówek. Twórz wątki, spraw, by działały szybciej.
- Podzieliłem pracę na części i każdy proces będzie działał na jednym z tych elementów równolegle.
- Dlaczego nie tworzysz wątków?
- Przepraszam, nie sądzę, żeby to było użyteczne. Możesz zabrać swój komputer, jeśli chcesz?
- Nie okej, w porządku, po prostu nie rozumiem, dlaczego nie używasz nici?
- Dziękuję za komputer. :) Dzieliłem już pracę na części i tworzę równolegle procesy do pracy nad tymi kawałkami. Wszystkie rdzenie procesora zostaną w pełni wykorzystane. Mógłbym to zrobić z wątkami zamiast procesów; ale Node ma ten sposób i moja szefowa Parth Thakkar chce, żebym używał Node.
- Dobra, daj mi znać, jeśli potrzebujesz innego komputera. : p
Jeśli utworzę 33 procesy, zamiast 32, program planujący systemu operacyjnego będzie wstrzymywać wątek, uruchamiać drugi, pauzować po kilku cyklach, ponownie uruchamiać drugi ... To niepotrzebny narzut. Nie chcę tego. W rzeczywistości w systemie z 32 rdzeniami nie chciałbym nawet tworzyć dokładnie 32 procesów, 31 może być ładniejsze . Ponieważ nie tylko moja aplikacja będzie działać w tym systemie. Pozostawienie trochę miejsca na inne rzeczy może być dobre, zwłaszcza jeśli mamy 32 pokoje.
Myślę, że jesteśmy teraz po tej samej stronie, jeśli chodzi o pełne wykorzystanie procesorów do zadań intensywnie wykorzystujących procesor .
- Hmm, Hasan, przepraszam, że trochę z ciebie kpiłem. Myślę, że teraz rozumiem cię lepiej. Ale jest jeszcze coś, na co potrzebuję wyjaśnienia: po co to całe zamieszanie związane z prowadzeniem setek wątków? Wszędzie czytałem, że wątki są znacznie szybsze do tworzenia i głupie niż procesy rozwidlania? Rozwidlasz procesy zamiast wątków i myślisz, że jest to najwyższa wartość, jaką można uzyskać w Node. Czy zatem Node nie jest odpowiedni do tego rodzaju pracy?
- Bez obaw, ja też jestem spoko. Wszyscy tak mówią, więc myślę, że jestem przyzwyczajony do ich słuchania.
-- Więc? Węzeł nie nadaje się do tego?
- Węzeł doskonale się do tego nadaje, chociaż wątki też mogą być dobre. Jeśli chodzi o narzut tworzenia wątków / procesów; w przypadku rzeczy, które często powtarzasz, liczy się każda milisekunda. Jednak tworzę tylko 32 procesy i zajmie to niewielką ilość czasu. Stanie się to tylko raz. To nie ma znaczenia.
- Kiedy więc chcę tworzyć tysiące wątków?
- Nigdy nie chcesz tworzyć tysięcy wątków. Jednak w systemie wykonującym pracę pochodzącą z zewnątrz, na przykład serwer WWW przetwarzający żądania HTTP; jeśli używasz wątku do każdego żądania, utworzysz wiele wątków, wiele z nich.
- Węzeł jest jednak inny? Dobrze?
-- Tak, dokładnie. W tym miejscu Node naprawdę błyszczy. Podobnie jak wątek jest znacznie lżejszy niż proces, wywołanie funkcji jest znacznie lżejsze niż wątek. Węzeł wywołuje funkcje, zamiast tworzyć wątki. W przykładzie serwera WWW każde przychodzące żądanie powoduje wywołanie funkcji.
-- Hmm interesujące; ale możesz uruchomić tylko jedną funkcję w tym samym czasie, jeśli nie używasz wielu wątków. Jak to działa, gdy do serwera WWW dociera wiele żądań w tym samym czasie?
- Masz całkowitą rację co do tego, jak funkcje działają pojedynczo, nigdy dwie równolegle. Mam na myśli to, że w jednym procesie działa tylko jeden zakres kodu w danym momencie. Harmonogram systemu operacyjnego nie przychodzi i nie wstrzymuje tej funkcji i nie przełącza się na inną, chyba że wstrzyma proces, aby dać czas na inny proces, a nie inny wątek w naszym procesie. (2)
- W takim razie w jaki sposób proces może obsłużyć jednocześnie 2 żądania?
- Proces może obsłużyć dziesiątki tysięcy żądań jednocześnie, o ile nasz system ma wystarczające zasoby (pamięć RAM, sieć itp.). Sposób działania tych funkcji to KLUCZOWA RÓŻNICA.
- Hmm, czy powinienem być teraz podekscytowany?
- Może :) Węzeł uruchamia pętlę w kolejce. W tej kolejce są nasze zadania, tj. Połączenia, które zaczęliśmy przetwarzać przychodzące żądania. Najważniejszym punktem jest sposób, w jaki projektujemy nasze funkcje do działania. Zamiast zaczynać przetwarzanie żądania i zmuszać dzwoniącego do czekania, aż zakończymy pracę, szybko kończymy naszą funkcję po wykonaniu odpowiedniej ilości pracy. Kiedy dochodzimy do punktu, w którym musimy poczekać, aż inny komponent wykona jakąś pracę i zwróci nam wartość, zamiast czekać na to, po prostu kończymy naszą funkcję, dodając resztę pracy do kolejki.
- Brzmi zbyt skomplikowanie?
- Nie, nie, mogę brzmieć skomplikowanie; ale sam system jest bardzo prosty i ma sens.
Teraz chcę przestać cytować dialog między tymi dwoma programistami i zakończyć moją odpowiedź po ostatnim krótkim przykładzie działania tych funkcji.
W ten sposób robimy to, co normalnie zrobiłby OS Scheduler. W pewnym momencie wstrzymujemy naszą pracę i pozwalamy innym wywołaniom funkcji (jak inne wątki w środowisku wielowątkowym) działać, dopóki nie otrzymamy ponownie naszej tury. Jest to znacznie lepsze niż pozostawienie pracy harmonogramowi systemu operacyjnego, który próbuje poświęcić czas każdemu wątkowi w systemie. Wiemy, co robimy, znacznie lepiej niż OS Scheduler i oczekuje się, że przestaniemy, kiedy powinniśmy przestać.
Poniżej znajduje się prosty przykład, w którym otwieramy plik i czytamy go, aby popracować nad danymi.
Synchroniczny sposób:
Open File
Repeat This:
Read Some
Do the work
Sposób asynchroniczny:
Open File and Do this when it is ready: // Our function returns
Repeat this:
Read Some and when it is ready: // Returns again
Do some work
Jak widać, nasza funkcja prosi system o otwarcie pliku i nie czeka na jego otwarcie. Kończy się, zapewniając kolejne kroki po przygotowaniu pliku. Kiedy wracamy, Node uruchamia inne wywołania funkcji w kolejce. Po przejrzeniu wszystkich funkcji pętla zdarzeń przechodzi do następnej tury ...
Podsumowując, Node ma zupełnie inny paradygmat niż programowanie wielowątkowe; ale to nie znaczy, że czegoś brakuje. W przypadku zadania synchronicznego (w którym możemy zdecydować o kolejności i sposobie przetwarzania) działa tak samo jak wielowątkowa równoległość. W przypadku zadań przychodzących z zewnątrz, takich jak żądania do serwera, jest po prostu lepsze.
(1) Chyba że budujesz biblioteki w innych językach, takich jak C / C ++, w którym to przypadku nadal nie tworzysz wątków do dzielenia zadań. Do tego rodzaju pracy masz dwa wątki, z których jeden będzie kontynuował komunikację z Node, podczas gdy drugi będzie wykonywał prawdziwą pracę.
(2) W rzeczywistości każdy proces Node ma wiele wątków z tych samych powodów, o których wspomniałem w pierwszym przypisie. Jednak nie jest to sposób, w jaki 1000 wątków wykonuje podobne prace. Te dodatkowe wątki służą do akceptowania zdarzeń IO i obsługi komunikatów międzyprocesowych.
UPDATE (jako odpowiedź na dobre pytanie w komentarzach)
@Mark, dziękuję za konstruktywną krytykę. W paradygmacie Node nigdy nie powinieneś mieć funkcji, których przetwarzanie zajmuje zbyt dużo czasu, chyba że wszystkie inne wywołania w kolejce są zaprojektowane tak, aby były uruchamiane jedna po drugiej. W przypadku zadań kosztownych obliczeniowo, jeśli spojrzymy na obraz w całości, zobaczymy, że nie jest to kwestia „Czy powinniśmy używać wątków czy procesów?” ale pytanie „Jak możemy podzielić te zadania w dobrze wyważony sposób na pod-zadania, które możemy wykonywać równolegle, wykorzystując wiele rdzeni procesora w systemie?” Powiedzmy, że przetworzymy 400 plików wideo w systemie z 8 rdzeniami. Jeśli chcemy przetwarzać jeden plik na raz, potrzebujemy systemu, który będzie przetwarzał różne części tego samego pliku, w którym to przypadku być może wielowątkowy system jednoprocesowy będzie łatwiejszy do zbudowania i jeszcze bardziej wydajny. Nadal możemy do tego używać Node, uruchamiając wiele procesów i przesyłając komunikaty między nimi, gdy konieczne jest współdzielenie stanu / komunikacja. Jak powiedziałem wcześniej, podejście wieloprocesowe w Node toa także wielowątkowe podejście do tego rodzaju zadań; ale nie więcej. Ponownie, jak powiedziałem wcześniej, sytuacja, w której Node świeci, ma miejsce, gdy mamy te zadania przychodzące do systemu z wielu źródeł, ponieważ jednoczesne utrzymywanie wielu połączeń jest znacznie lżejsze w Node w porównaniu z wątkiem na połączenie lub procesem na połączenie system.
Jeśli chodzi o setTimeout(...,0)
rozmowy; czasami może być wymagane zrobienie przerwy w czasochłonnym zadaniu, aby umożliwić połączeniom w kolejce udział w przetwarzaniu. Dzielenie zadań na różne sposoby może cię przed nimi uchronić; ale nadal nie jest to tak naprawdę hack, to tylko sposób, w jaki działają kolejki zdarzeń. Również użycie process.nextTick
do tego celu jest o wiele lepsze, ponieważ podczas używania setTimeout
obliczenia i sprawdzanie minionego czasu będą konieczne, a process.nextTick
jest to po prostu to, czego naprawdę chcemy: „Hej zadanie, wróć na koniec kolejki, wykorzystałeś swój udział! "