Jaka jest różnica między architekturami: asynchronicznymi, nieblokującymi, bazowymi na zdarzeniach?

84
  1. Jaka jest różnica pomiędzy:

    • Asynchroniczne ,
    • Non-Blocking i
    • Architektury bazujące na wydarzeniach ?
  2. Czy coś może być zarówno asynchroniczne, jak i nieblokujące (i oparte na zdarzeniach )?

  3. Co jest najważniejsze w programowaniu, aby mieć coś: asynchroniczne, nieblokujące i / lub oparte na zdarzeniach (lub wszystkie 3)?

Byłoby świetnie, gdybyś mógł podać przykłady.

To pytanie jest zadawane, ponieważ czytałem ten świetny artykuł StackOverflow na podobny temat, ale nie zawiera on odpowiedzi na moje pytania powyżej.

nickb
źródło

Odpowiedzi:

91

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.

Rohit Karlupia
źródło
2
Dobra uwaga na temat braku blokowania wymagającego ciągłego odpytywania, podczas gdy asynchronizacja może być oparta na wypychaniu.
Alexander Torstling,
Zdefiniowałeś synchroniczne jako otrzymywanie natychmiastowej odpowiedzi, ale kiedy wyszukuję synchronizację w Google, wszystkie słowniki definiują to jako „dzieje się w tym samym czasie”, a nie „natychmiastową odpowiedź”.
IntelliData
4
W jaki sposób jestem blokowany, gdy wysyłam wiadomość e-mail, ale nie oczekuję odpowiedzi? Mogę zająć się własnymi sprawami, czekając na odpowiedź.
Koray Tugay
20

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.

supercat
źródło
5

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.

  1. Tak, kod może być asynchroniczny, nieblokujący ORAZ oparty na zdarzeniach.

  2. 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ć.

user924272
źródło
** AKTUALIZACJA ** Po zabawie z Go i zapoznaniu się z kanałami i procedurami go, muszę powiedzieć, że tak naprawdę lubię uczynić mój kod bardziej współbieżnym, ponieważ konstrukcje języka biorą cały ból z autorów Sadystów. Mamy „bezpieczne słowo” w świecie przetwarzania asynchronicznego - jest nim „Idź!”
user924272
4

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.

ewernli
źródło
2

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.

arootbeer
źródło
Czy Twój ostatni akapit nie zaprzecza stwierdzeniu, że „architektura asynchroniczna jest ... nieblokująca”
nickb
Wydaje mi się, że nie poradziłem sobie zbyt dobrze z odniesieniem się do części twojego pytania dotyczącej „definicji”; Opublikuję aktualizację. Ale nie, natura wykonania jednowątkowego polega na tym, że każda operacja jest z natury blokowana podczas działania , co sprawia, że ​​asynchronia jest jeszcze bardziej użyteczna.
arootbeer