Asynchroniczny
Asynchroniczny dosłownie oznacza brak synchronizacji. Poczta e-mail jest asynchroniczna. Wysyłasz wiadomość, nie spodziewasz się odpowiedzi TERAZ. Ale to nie jest blokowanie. Zasadniczo oznacza to architekturę, w której „komponenty” wysyłają do siebie komunikaty, nie oczekując natychmiastowej odpowiedzi. Żądania HTTP są synchroniczne. Wyślij zapytanie i otrzymaj odpowiedź.
Non-Blocking
Ten termin jest najczęściej używany w przypadku IO. Oznacza to, że kiedy wykonujesz wywołanie systemowe, powróci ono natychmiast z jakimkolwiek wynikiem, bez uśpienia wątku (z dużym prawdopodobieństwem). Na przykład nieblokujące wywołania odczytu / zapisu zwracają wszystko, co mogą, i oczekują, że wywołujący ponownie wykona wywołanie. Na przykład try_lock jest połączeniem nieblokującym. Zostanie zablokowany tylko wtedy, gdy można uzyskać zamek. Zwykła semantyka wywołań systemowych to blokowanie. read będzie czekał, aż będzie miał jakieś dane i przełączy wątek wywołujący w stan uśpienia.
Baza zdarzeń
Ten termin pochodzi z libevent. nieblokujące wywołania odczytu / zapisu same w sobie są bezużyteczne, ponieważ nie mówią Ci „kiedy” należy do nich oddzwonić (ponów próbę). select / epoll / IOCompletionPort itp. to różne mechanizmy sprawdzania z systemu operacyjnego, „kiedy” te wywołania powinny zwrócić „interesujące” dane. libevent i inne tego typu biblioteki zapewniają otoki dla tych funkcji monitorowania zdarzeń dostarczanych przez różne systemy operacyjne i zapewniają spójny interfejs API do pracy, który działa w różnych systemach operacyjnych. Nieblokujące IO idzie w parze z bazą zdarzeń.
Myślę, że te terminy się pokrywają. Na przykład protokół HTTP jest synchroniczny, ale implementacja HTTP korzystająca z nieblokujących operacji we / wy może być asynchroniczna. Ponownie nieblokujące wywołanie API, takie jak odczyt / zapis / try_lock, jest synchroniczne (natychmiast daje odpowiedź), ale „obsługa danych” jest asynchroniczna.
W sprzęcie asynchronicznym kod prosi jakąś jednostkę o zrobienie czegoś i może robić inne rzeczy, gdy akcja jest wykonywana; po zakończeniu działania jednostka zazwyczaj sygnalizuje kod w jakiś sposób. Architektura nieblokująca będzie odnotowywać spontanicznie występujące akcje, którymi kod może być zainteresowany, i pozwoli kodowi zapytać, jakie takie działania miały miejsce, ale kod zda sobie sprawę z takich działań tylko wtedy, gdy wyraźnie o nie zapyta. Architektura oparta na zdarzeniach pozytywnie powiadamia kod o spontanicznych zdarzeniach.
Rozważ port szeregowy, z którego kod będzie chciał odebrać 1000 bajtów.
W architekturze z blokowaniem i odczytem kod będzie czekał, aż nadejdzie 1000 bajtów lub zdecyduje się zrezygnować.
W architekturze asynchronicznego odczytu kod poinformuje sterownik, że chce 1000 bajtów, i zostanie powiadomiony, gdy nadejdzie 1000 bajtów.
W architekturze nieblokującej kod może w dowolnym momencie zapytać o liczbę przybyłych bajtów i odczytać dowolne lub wszystkie takie dane, gdy uzna to za stosowne, ale jedynym sposobem, w jaki może wiedzieć, kiedy nadeszły wszystkie dane, jest zapytanie; jeśli kod chce dowiedzieć się w ciągu ćwierć sekundy, kiedy nadszedł 1000-ty bajt, musi sprawdzać mniej więcej co kwadrans.
W architekturze opartej na zdarzeniach sterownik portu szeregowego powiadomi aplikację za każdym razem, gdy nadejdą jakiekolwiek dane. Sterownik nie będzie wiedział, ile bajtów chce aplikacja, więc aplikacja musi być w stanie obsłużyć powiadomienia o kwotach, które są mniejsze lub większe niż żąda aplikacja.
źródło
A więc odpowiadając na pierwsze i drugie pytanie:
Nieblokowanie jest w rzeczywistości tym samym, co asynchroniczne - wykonujesz wywołanie, a później otrzymasz wynik, ale gdy to się dzieje, możesz zrobić coś innego. Blokowanie jest odwrotne. Przed kontynuowaniem podróży czekasz, aż telefon powróci.
Teraz kod asynchroniczny / nieblokujący brzmi absolutnie fantastycznie i tak jest. Ale mam słowa ostrzeżenia. Async / Non-blocking są świetne podczas pracy w ograniczonych środowiskach, takich jak telefon komórkowy ... rozważ ograniczony procesor / pamięć. Jest to również dobre dla programowania front-end, w którym kod musi w jakiś sposób reagować na widżet interfejsu użytkownika.
Asynchronizacja ma fundamentalne znaczenie dla tego, jak wszystkie systemy operacyjne muszą działać - robią za ciebie gówno w tle i budzą twój kod, gdy zrobią to, o co prosiłeś, a kiedy to połączenie się nie powiedzie, powiedziano ci, że tak się nie stało działają albo przez wyjątek, albo przez jakiś rodzaj kodu powrotu / obiektu błędu.
W momencie, gdy kod prosi o coś, co zajmie trochę czasu, aby odpowiedzieć, Twój system operacyjny wie, że może być zajęty robieniem innych rzeczy. Twój kod - proces, wątek lub odpowiednik, bloki. Twój kod jest całkowicie nieświadomy tego, co jeszcze dzieje się w systemie operacyjnym, gdy czeka na to połączenie sieciowe, lub gdy czeka na odpowiedź z żądania HTTP, lub gdy czeka na odczyt / zapis pliku, i wkrótce. Twój kod mógłby „po prostu” czekać na kliknięcie myszą. To, co faktycznie działo się w tym czasie, to to, że system operacyjny bezproblemowo zarządza, planuje i reaguje na „zdarzenia” - rzeczy, na które system operacyjny zwraca uwagę, takie jak zarządzanie pamięcią, I / O (klawiatura, mysz, dysk, internet), inne zadania, usuwanie awarii itp.
Systemy operacyjne są cholernie twarde. Są naprawdę dobrzy w ukrywaniu wszystkich skomplikowanych elementów asynchronicznych / nieblokujących przed programistą. I w ten sposób większość programistów dotarła do miejsca, w którym jesteśmy dzisiaj dzięki oprogramowaniu. Teraz zbliżamy się do limitów procesora, ludzie mówią, że można zrobić coś równolegle, aby poprawić wydajność. Oznacza to, że Async / non-blocking wydaje się być bardzo korzystną rzeczą do zrobienia i tak, jeśli twoje oprogramowanie tego wymaga, zgadzam się.
Jeśli piszesz serwer WWW zaplecza, postępuj ostrożnie. Pamiętaj, że możesz znacznie taniej skalować w poziomie. Netflix / Amazon / Google / Facebook są jednak oczywistymi wyjątkami od tej reguły, tylko dlatego, że taniej im wychodzi używanie mniejszej ilości sprzętu.
Powiem ci, dlaczego kod asynchroniczny / nieblokujący to koszmar z systemami zaplecza ...
1) To staje się odmowa usługi w zakresie produktywności ... musisz dużo więcej myśleć i po drodze popełniasz wiele błędów.
2) Ślady stosu w kodzie reaktywnym stają się nieczytelne - trudno powiedzieć, co nazywało się co, kiedy, dlaczego i jak. Powodzenia w debugowaniu.
3) Musisz więcej pomyśleć o tym, jak coś zawodzi, zwłaszcza gdy wiele rzeczy wraca do normy, jak je wysłałeś. W starym świecie robiłeś jedną rzecz na raz.
4) Trudniej jest przetestować.
5) Trudniej jest utrzymać.
6) To bolesne. Programowanie powinno być przyjemne i przyjemne. Tylko masochiści lubią ból. Ludzie, którzy piszą ramy współbieżne / reaktywne, są sadystami.
I tak, napisałem zarówno sync, jak i async. Wolę synchronizację, ponieważ 99,99 aplikacji zaplecza może sobie poradzić z tym paradygmatem. Aplikacje frontonu wymagają reaktywnego kodu, bez pytania, i zawsze tak było.
Tak, kod może być asynchroniczny, nieblokujący ORAZ oparty na zdarzeniach.
Najważniejszą rzeczą w programowaniu jest upewnienie się, że kod działa i reaguje w akceptowalnym czasie. Trzymaj się tej kluczowej zasady i nie możesz się pomylić.
źródło
Dla mnie nieblokowanie oznacza, że wykonanie akcji w wątku nie zależy od wykonania innych wątków, w szczególności nie wymaga sekcji krytycznej.
Asynchroniczne oznacza, że wykonanie odbywa się poza przepływem obiektu wywołującego i jest potencjalnie odroczone. Wykonanie zwykle odbywa się w innym wątku.
Odczytywanie współbieżnych danych nie blokuje (nie ma potrzeby blokowania), ale jest synchroniczne. I odwrotnie, współbieżne zapisywanie danych w sposób synchroniczny jest blokowaniem (wymaga blokady na wyłączność). Sposobem na uczynienie go nieblokującym z perspektywy głównego przepływu jest uczynienie zapisów asynchronicznymi i odroczenie ich wykonania.
Pojęcie zdarzenia to coś innego, co z grubsza oznacza, że jesteś informowany, gdy coś się wydarzy. Jeśli zapisy zostały wykonane asynchronicznie, może zostać wywołane zdarzenie w celu poinformowania innych części systemu po wykonaniu zapisu. Pozostałe części odpowiedzą na zdarzenie. System można zbudować wyłącznie na zdarzeniach jako jedynym sposobie komunikacji między komponentami (pomyśl o modelu aktora), ale nie musi tak być.
Te trzy terminy są ze sobą powiązane, ale dla mnie są to różne pojęcia. Może się jednak zdarzyć, że ludzie używają ich w nieco wymienny sposób.
źródło
Generalnie, non-blocking architektura opiera się na wywołań metod, że choć może nie realizowanych przez długi czas na pracownika wątku, nie zablokuje rozmów nici. Jeśli wątek wywołujący musi uzyskać informacje o zadaniu wykonywanym przez wątek roboczy lub na podstawie tego zadania, wykonanie tego należy do wątku wywołującego.
Architektura oparta na zdarzeniach opiera się na koncepcji kodu wykonywanego w odpowiedzi na wywoływane zdarzenia. Czas wykonania kodu zazwyczaj nie jest deterministyczny, ale zdarzenia mogą wywoływać metody blokujące; tylko dlatego, że system jest oparty na zdarzeniach, nie oznacza, że wszystko, co robi, nie blokuje.
Ogólnie architektura asynchroniczna jest architekturą nieblokującą opartą na zdarzeniach.
W przypadku wywołania asynchronicznego programy obsługi zdarzeń są rejestrowane w interfejsie API zapewniającym usługi synchronizacji w celu powiadomienia osoby dzwoniącej, że wydarzyło się coś, czym jest zainteresowany. Następnie wywołanie natychmiast wraca (zachowanie nieblokujące), a wywołujący może kontynuować wykonywanie. Gdy zdarzenia są wysyłane z powrotem do procesu wywołującego, będą obsługiwane w jakimś wątku w tym procesie.
Ważne jest, aby zrozumieć, czy zdarzenia będą obsługiwane w tym samym wątku, czy nie, ponieważ wpłynie to na nieblokujący charakter wykonania, ale osobiście nie znam żadnych bibliotek, które wykonują asynchroniczne zarządzanie wykonywaniem w jednym wątku.Usunąłem powyższy akapit, ponieważ nie jest do końca poprawny, jak stwierdzono. Chciałem powiedzieć, że chociaż operacje w systemie nie blokują się, takie jak wywoływanie funkcji systemu operacyjnego i kontynuowanie wykonywania, natura wykonywania jednowątkowego oznacza, że gdy zdarzenia są uruchamiane, będą konkurować z inne zadania przetwarzania związane z czasem obliczania w wątku.
źródło