Jaka jest różnica między połączeniami asynchronicznymi i nieblokującymi? Także między blokowaniem a połączeniami synchronicznymi (proszę z przykładami)?
Jaka jest różnica między połączeniami asynchronicznymi i nieblokującymi? Także między blokowaniem a połączeniami synchronicznymi (proszę z przykładami)?
Odpowiedzi:
W wielu okolicznościach są to różne nazwy dla tej samej rzeczy, ale w niektórych kontekstach są zupełnie inne. Więc to zależy. Terminologia nie jest stosowana w całkowicie spójny sposób w całej branży oprogramowania.
Na przykład w klasycznym API gniazd, gniazdo nieblokujące to takie, które po prostu natychmiast zwraca specjalny komunikat o błędzie „blokuje”, podczas gdy gniazdo blokujące blokuje. Musisz użyć oddzielnej funkcji, takiej jak select
lub, poll
aby dowiedzieć się, kiedy jest dobry moment, aby spróbować ponownie.
Ale gniazda asynchroniczne (obsługiwane przez gniazda systemu Windows) lub asynchroniczny wzorzec operacji we / wy używany w .NET są wygodniejsze. Wywołujesz metodę rozpoczęcia operacji, a środowisko oddzwania po zakończeniu. Nawet tutaj istnieją podstawowe różnice. Asynchroniczne gniazda Win32 „zbierają” swoje wyniki w określonym wątku GUI, przekazując komunikaty systemu Windows, podczas gdy asynchroniczne operacje we / wy platformy .NET działają bez wątków (nie wiesz, w jakim wątku zostanie wywołane Twoje wywołanie zwrotne).
Dlatego nie zawsze oznaczają to samo. Aby destylować przykład gniazda, możemy powiedzieć:
Synchroniczny / asynchroniczny ma opisać relację między dwoma modułami.
blokowanie / nieblokowanie ma opisać sytuację jednego modułu.
Przykład:
moduł X: „I”.
Moduł Y: „księgarnia”.
X pyta Y: czy masz książkę o nazwie „c ++ primer”?
1) blokowanie: zanim Y odpowie X, X nadal czeka tam na odpowiedź. Teraz X (jeden moduł) blokuje. X i Y to dwa wątki lub dwa procesy, jeden wątek czy jeden proces? nie wiemy.
2) bez blokowania: zanim Y odpowie X, X po prostu tam wychodzi i robi inne rzeczy. X może wracać co dwie minuty, aby sprawdzić, czy Y zakończył pracę? Czy X nie wróci, dopóki Y do niego nie zadzwoni? Nie wiemy Wiemy tylko, że X może zrobić inne rzeczy, zanim Y zakończy swoją pracę. Tutaj X (jeden moduł) nie jest blokujący. X i Y to dwa wątki lub dwa procesy, czy jeden proces? nie wiemy. ALE jesteśmy pewni, że X i Y nie mogą być jednym wątkiem.
3) synchroniczny: zanim Y odpowie X, X czeka tam na odpowiedź. Oznacza to, że X nie może kontynuować, dopóki Y nie zakończy swojego zadania. Teraz mówimy: X i Y (dwa moduły) są synchroniczne. X i Y to dwa wątki lub dwa procesy, jeden wątek czy jeden proces? nie wiemy.
4) asynchroniczny: zanim Y odpowie na X, X tam wychodzi i X może wykonywać inne zadania. X nie wróci, dopóki Y go nie wezwie. Teraz mówimy: X i Y (dwa moduły) są asynchroniczne. X i Y to dwa wątki lub dwa procesy, czy jeden proces? nie wiemy. ALE jesteśmy pewni, że X i Y nie mogą być jednym wątkiem.
Proszę zwrócić uwagę na dwa odważne zdania powyżej. Dlaczego pogrubione zdanie w 2) zawiera dwa przypadki, a pogrubione zdanie w 4) zawiera tylko jeden przypadek? Jest to klucz do różnicy między nieblokującym a asynchronicznym.
Oto typowy przykład dotyczący nieblokowania i synchronizacji:
// thread X
while (true)
{
msg = recv(Y, NON_BLOCKING_FLAG);
if (msg is not empty)
{
break;
}
sleep(2000); // 2 sec
}
// thread Y
// prepare the book for X
send(X, book);
Widać, że ten projekt nie jest blokujący (można powiedzieć, że przez większość czasu ta pętla robi coś nonsensownego, ale w oczach procesora X działa, co oznacza, że X nie jest blokujący), podczas gdy X i Y są synchroniczne, ponieważ X może '' kontynuuję robienie innych rzeczy (X nie może wyskoczyć z pętli), dopóki nie pobierze książki z Y.
Zwykle w tym przypadku spraw, aby blokowanie X było znacznie lepsze, ponieważ nieblokowanie spędza dużo zasobów na głupią pętlę. Ale ten przykład jest dobry, aby pomóc ci zrozumieć fakt: nieblokowanie nie oznacza asynchronicznego.
Te cztery słowa sprawiają, że łatwo się mylić, powinniśmy pamiętać, że cztery słowa służą do projektowania architektury. Uczenie się, jak zaprojektować dobrą architekturę, jest jedynym sposobem na ich rozróżnienie.
Na przykład możemy zaprojektować taką architekturę:
// Module X = Module X1 + Module X2
// Module X1
while (true)
{
msg = recv(many_other_modules, NON_BLOCKING_FLAG);
if (msg is not null)
{
if (msg == "done")
{
break;
}
// create a thread to process msg
}
sleep(2000); // 2 sec
}
// Module X2
broadcast("I got the book from Y");
// Module Y
// prepare the book for X
send(X, book);
W przykładzie tutaj możemy to powiedzieć
Jeśli potrzebujesz, możesz również opisać wątki utworzone w X1 za pomocą czterech słów.
Najważniejsze rzeczy to: kiedy używamy synchronicznego zamiast asynchronicznego? kiedy używamy blokowania zamiast nieblokowania?
Dlaczego Nginx nie blokuje? Dlaczego Apache blokuje?
Aby dokonać dobrego wyboru, musisz przeanalizować swoje potrzeby i przetestować wydajność różnych architektur. Nie ma takiej architektury, która byłaby odpowiednia dla różnych potrzeb.
Umieszczając to pytanie w kontekście NIO i NIO.2 w java 7, asynchroniczne we / wy jest o jeden krok bardziej zaawansowane niż nieblokowanie. W przypadku nieblokujących połączeń Java NIO można ustawić wszystkie kanały (SocketChannel, ServerSocketChannel, FileChannel itp.) Jako takie, wywołując AbstractSelectableChannel.configureBlocking(false)
. Jednak po powrocie tych wywołań IO prawdopodobnie nadal będziesz musiał kontrolować kontrole, takie jak czy i kiedy czytać / pisać ponownie itp.
Na przykład
while (!isDataEnough()) {
socketchannel.read(inputBuffer);
// do something else and then read again
}
Dzięki asynchronicznemu interfejsowi API w Javie 7 te elementy sterujące mogą być wykonywane w bardziej uniwersalny sposób. Jednym z 2 sposobów jest użycie CompletionHandler
. Zauważ, że oba read
połączenia nie są blokowane.
asyncsocket.read(inputBuffer, 60, TimeUnit.SECONDS /* 60 secs for timeout */,
new CompletionHandler<Integer, Object>() {
public void completed(Integer result, Object attachment) {...}
public void failed(Throwable e, Object attachment) {...}
}
}
FileChannel
nie można wybrać i nie można go skonfigurować do nieblokowania.
Jak zapewne widać na podstawie wielu różnych (i często wzajemnie się wykluczających) odpowiedzi, zależy to od tego, kogo zapytasz. Na niektórych obszarach warunki są synonimami. Lub mogą one odnosić się do dwóch podobnych pojęć:
W obu przypadkach chodzi o to, aby program nie był blokowany w oczekiwaniu na zakończenie powolnego procesu - jedyną prawdziwą różnicą jest sposób, w jaki program ma reagować. Które określenie odnosi się do którego zmienia się również z programisty na programistę, języka na język lub platformy na platformę. Lub terminy mogą odnosić się do zupełnie różnych pojęć (takich jak użycie synchroniczne / asynchroniczne w odniesieniu do programowania wątków).
Przepraszam, ale nie wierzę, że istnieje jedna prawidłowa odpowiedź, która jest prawdziwa na całym świecie.
A nonblocking powraca połączeń bezpośrednio z tym, co dane są dostępne: pełny numer bajtów wymagane, mniej lub wcale.
Połączenie asynchroniczne żąda przeniesienia, które zostanie wykonane w całości (całości), ale zostanie zakończone w przyszłości.
Nieblokujący: Ta funkcja nie będzie czekać na stosie.
Asynchroniczny: praca może być kontynuowana w imieniu wywołania funkcji, gdy to wywołanie opuści stos
Synchroniczny jest zdefiniowany jako działający w tym samym czasie.
Asynchroniczny jest zdefiniowany jako nie dzieje się w tym samym czasie.
To powoduje pierwsze zamieszanie. Synchroniczny to tak naprawdę tak zwany równoległy. Chociaż asynchroniczny jest sekwencyjny, zrób to, a następnie zrób to.
Teraz cały problem dotyczy modelowania zachowania asynchronicznego, ponieważ masz operację, która wymaga odpowiedzi innej, zanim będzie mogła się rozpocząć. Jest to zatem problem z koordynacją. Skąd będziesz wiedzieć, że możesz teraz rozpocząć tę operację?
Najprostsze rozwiązanie to blokowanie.
Blokowanie ma miejsce, gdy po prostu zdecydujesz się poczekać na wykonanie innej czynności i odpowiedzieć, zanim przejdziesz do operacji, która tego wymagała.
Więc jeśli chcesz położyć masło na grzance, a zatem najpierw musisz wznieść toast za hodowlę. Sposób, w jaki je koordynujesz, polega na tym, że najpierw wznosisz toast za hodowlę, a następnie wpatrujesz się w toster bez końca, aż wyskoczy toast, a następnie zaczniesz nakładać na nie masło.
To najprostsze rozwiązanie i działa bardzo dobrze. Nie ma prawdziwego powodu, aby go nie używać, chyba że zdarzają się również inne rzeczy, które musisz robić, które nie wymagają koordynacji z operacjami. Na przykład zmywanie niektórych potraw. Po co czekać bezczynnie, wpatrując się w toster ciągle, aż tost wyskoczy, skoro wiesz, że zajmie to trochę czasu i możesz umyć całe naczynie, gdy się skończy?
Właśnie tutaj wchodzą w grę dwa inne rozwiązania, znane odpowiednio jako nieblokujące i asynchroniczne.
Nieblokowanie ma miejsce wtedy, gdy zdecydujesz się wykonać inne niezwiązane rzeczy podczas oczekiwania na wykonanie operacji. Sprawdzanie dostępności odpowiedzi według własnego uznania.
Zamiast patrzeć na toster, żeby wyskoczył. Idziesz i myjesz całe naczynie. A potem zerkasz na toster, aby zobaczyć, czy grzanki się wyskoczyły. Jeśli nie, idź umyć kolejne naczynie, sprawdzając tostera między każdym naczyniem. Kiedy zobaczysz, że tosty wyskoczyły, przestajesz myć naczynia, a zamiast tego bierzesz tosty i przystępujesz do nakładania na nie masła.
Konieczność ciągłego sprawdzania tostów może być denerwująca, wyobraź sobie, że toster znajduje się w innym pokoju. Pomiędzy naczyniami marnujesz czas na chodzenie do innego pokoju, aby sprawdzić toast.
Nadchodzi asynchronicznie.
Asynchroniczny ma miejsce wtedy, gdy wybierasz wykonywanie innych niezwiązanych ze sobą czynności podczas oczekiwania na wykonanie operacji. Zamiast sprawdzania tego, przekazujesz pracę sprawdzania do czegoś innego, może to być sama operacja lub obserwator, i masz to powiadomienie i być może przeszkadza, gdy odpowiedź jest dostępna, abyś mógł przejść do innej operacji, która potrzebowałem tego.
To dziwna terminologia. Nie ma to większego sensu, ponieważ wszystkie te rozwiązania są sposobami do asynchronicznej koordynacji zależnych zadań. Dlatego wolę nazywać to wydarzeniem.
Więc w tym przypadku decydujesz się na ulepszenie swojego tostera, aby emitował sygnał dźwiękowy po zakończeniu grzanek. Zdarza się, że ciągle słuchasz, nawet podczas zmywania naczyń. Po usłyszeniu sygnału dźwiękowego ustawiasz się w kolejce w pamięci, że gdy tylko skończysz myć swoje obecne danie, zatrzymasz się i pójdziesz położyć masło na grzance. Możesz też przerwać mycie bieżącego naczynia i od razu zająć się grzanką.
Jeśli nie możesz usłyszeć sygnału dźwiękowego, możesz poprosić partnera o obserwowanie tostera i przyjście powiedzieć, kiedy tost jest gotowy. Twój partner może sam wybrać jedną z powyższych trzech strategii, aby koordynować swoje zadanie polegające na obserwowaniu tostera i informowaniu go, kiedy będą gotowe.
Podsumowując, dobrze jest zrozumieć, że chociaż nieblokowanie i asynchronizacja (lub to, co wolę nazywać eventem), pozwalają ci robić inne rzeczy podczas oczekiwania, nie masz też. Możesz stale sprawdzać status połączenia nieblokującego, nie robiąc nic więcej. Jest to często gorsze niż blokowanie (jak patrzenie na toster, potem odwrócenie go, a potem powrót do niego, aż się skończy), więc wiele nieblokujących interfejsów API umożliwia przejście z trybu blokowania. W przypadku eventu możesz po prostu czekać bezczynnie, aż otrzymasz powiadomienie. Minusem w tym przypadku jest to, że dodanie powiadomienia było na początku skomplikowane i potencjalnie kosztowne. Trzeba było kupić nowy toster z funkcją beep lub przekonać partnera, aby go obserwował.
I jeszcze jedno, musisz zdać sobie sprawę z kompromisów, które zapewniają wszystkie trzy. Jedno nie jest oczywiście lepsze od innych. Pomyśl o moim przykładzie. Jeśli twój toster jest tak szybki, nie będziesz miał czasu, aby umyć naczynie, nawet nie zacząć go myć, tak szybki jest toster. Rozpoczęcie pracy nad czymś innym to strata czasu i wysiłku. Blokowanie wystarczy. Podobnie, jeśli mycie naczynia potrwa 10 razy dłużej niż opiekanie. Musisz zadać sobie pytanie, co jest ważniejsze do zrobienia? Tosty mogą stać się zimne i twarde do tego czasu, nie warto, blokowanie też się sprawdzi. Lub powinieneś wybrać szybsze rzeczy do zrobienia, czekając. Jest bardziej oczywiste, ale moja odpowiedź jest już dość długa. Chodzi mi o to, aby pomyśleć o tym wszystkim i o złożoności wdrażania każdego z nich, aby zdecydować, czy warto, a jeśli „
Edytować:
Chociaż jest to już długie, chcę, aby było kompletne, więc dodam jeszcze dwa punkty.
1) Często istnieje również czwarty model znany jako multipleksowany . To wtedy, gdy czekasz na jedno zadanie, zaczynasz inne, a gdy czekasz na oba, zaczynasz jeszcze jedno itd., Dopóki nie zaczniesz wielu zadań, a potem zaczniesz czekać bezczynnie, ale na wszystkich im. Gdy tylko zostanie to zrobione, możesz przystąpić do obsługi jego odpowiedzi, a następnie powrócić do oczekiwania na pozostałych. Nazywa się to multipleksowaniem, ponieważ podczas oczekiwania musisz sprawdzać każde zadanie jedno po drugim, aby sprawdzić, czy zostało wykonane, ad vitam, dopóki nie zostanie wykonane. To trochę rozszerzenie poza zwykłym nieblokowaniem.
W naszym przykładzie byłoby to jak uruchomienie tostera, potem zmywarki, mikrofalówki itp. A potem czekanie na którykolwiek z nich. Gdybyś sprawdził toster, aby zobaczyć, czy jest zrobiony, jeśli nie, sprawdziłbyś zmywarkę, jeśli nie, kuchenkę mikrofalową i jeszcze raz.
2) Mimo że uważam, że to duży błąd, synchroniczny często oznacza jedną rzecz na raz. I asynchroniczne wiele rzeczy na raz. Zatem zobaczysz synchroniczne blokowanie i nieblokowanie używane w odniesieniu do blokowania i nieblokowania. A asynchroniczne blokowanie i nieblokowanie odnosi się do multipleksowania i zdarzeń.
Naprawdę nie rozumiem, jak się tam dostaliśmy. Ale jeśli chodzi o operacje we / wy i obliczenia, synchroniczne i asynchroniczne często odnoszą się do tego, co jest lepiej znane jako nie nakładające się i nakładające się. Oznacza to, że asynchroniczne oznacza, że operacje we / wy i obliczenia nakładają się, inaczej dzieje się jednocześnie. Chociaż synchroniczne oznacza, że tak nie jest, dzieje się to sekwencyjnie. W przypadku synchronicznego nieblokowania oznacza to, że nie uruchamiasz innego We / Wy ani obliczeń, po prostu jesteś zajęty oczekiwaniem i symulowaniem połączenia blokującego. Chciałbym, żeby ludzie przestali niewłaściwie używać synchronicznych i asynchronicznych w ten sposób. Więc nie zachęcam tego.
Blokowanie połączenia: Kontrola powraca tylko po zakończeniu połączenia.
Nieblokujące połączenie: Kontrola natychmiast wraca. Później OS w jakiś sposób powiadamia proces o zakończeniu połączenia.
Program synchroniczny : program korzystający z połączeń blokujących . Aby nie zawiesić się podczas połączenia, musi mieć 2 lub więcej wątków (dlatego nazywa się Synchroniczny - wątki działają synchronicznie).
Program asynchroniczny : program, który używa wywołań nieblokujących . Może mieć tylko 1 wątek i nadal pozostać interaktywny.
Różnią się tylko pisownią. Nie ma różnicy w tym, do czego się odnoszą. Mówiąc technicznie, można powiedzieć, że różnią się naciskiem. Brak blokowania odnosi się do przepływu sterowania (nie blokuje). Asynchroniczny oznacza, kiedy zdarzenie \ dane jest obsługiwane (nie synchronicznie).
Modele blokujące wymagają, aby aplikacja inicjująca blokowała się po uruchomieniu I / O. Oznacza to, że nie można jednocześnie nakładać przetwarzania i operacji we / wy. Synchroniczny nieblokujący model umożliwia nakładanie się przetwarzania i operacji we / wy, ale wymaga, aby aplikacja sprawdzała regularnie stan operacji we / wy. Pozostawia to asynchroniczne nieblokujące We / Wy, co pozwala na nakładanie się przetwarzania i We / Wy, w tym powiadamianie o zakończeniu We / Wy.
Blokowanie: kontrola powraca do wywoływania precesji po zakończeniu przetwarzania operacji podstawowej (synchronizacji lub asynchronizacji)
Bez blokowania: kontrola powraca do przetwarzania natychmiast po wywołaniu