Czy nieblokujące operacje we / wy są naprawdę szybsze niż wielowątkowe blokowanie we / wy? W jaki sposób?

119

Przeszukałem Internet pod kątem niektórych szczegółów technicznych dotyczących blokowania we / wy i nieblokujących operacji we / wy i znalazłem kilka osób, które twierdziły, że nieblokujące we / wy będą szybsze niż blokowanie we / wy. Na przykład w tym dokumencie .

Jeśli używam blokowania I / O, to oczywiście wątek, który jest aktualnie zablokowany, nie może zrobić nic innego ... Ponieważ jest zablokowany. Ale gdy tylko wątek zacznie być blokowany, system operacyjny może przełączyć się na inny wątek i nie przełączyć się z powrotem, dopóki nie zostanie coś do zrobienia dla zablokowanego wątku. Tak długo, jak istnieje inny wątek w systemie, który wymaga procesora i nie jest blokowany, nie powinno być więcej czasu bezczynności procesora w porównaniu z podejściem nieblokującym opartym na zdarzeniach, prawda?

Oprócz skrócenia czasu bezczynności procesora widzę jeszcze jedną opcję zwiększenia liczby zadań, które komputer może wykonać w danym przedziale czasowym: Zmniejszenie obciążenia związanego z przełączaniem wątków. Ale jak to zrobić? Czy narzut jest wystarczająco duży, aby pokazać wymierne efekty? Oto pomysł, jak mogę to sobie wyobrazić:

  1. Aby załadować zawartość pliku, aplikacja deleguje to zadanie do opartej na zdarzeniach struktury we / wy, przekazując funkcję wywołania zwrotnego wraz z nazwą pliku
  2. Struktura zdarzeń jest delegowana do systemu operacyjnego, który programuje kontroler DMA dysku twardego w celu zapisania pliku bezpośrednio w pamięci
  3. Struktura zdarzeń umożliwia uruchomienie dalszego kodu.
  4. Po zakończeniu kopiowania dysku do pamięci kontroler DMA powoduje przerwanie.
  5. Procedura obsługi przerwań systemu operacyjnego powiadamia opartą na zdarzeniach strukturę we / wy o całkowitym załadowaniu pliku do pamięci. Jak to się dzieje? Używając sygnału?
  6. Kod, który jest obecnie uruchamiany w ramach struktury we / wy zdarzenia, kończy się.
  7. Struktura we / wy oparta na zdarzeniach sprawdza swoją kolejkę i widzi komunikat systemu operacyjnego z kroku 5 i wykonuje wywołanie zwrotne otrzymane w kroku 1.

Czy tak to działa? Jeśli tak nie jest, jak to działa? Oznacza to, że system zdarzeń może działać bez konieczności jawnego dotykania stosu (na przykład prawdziwy harmonogram, który musiałby wykonać kopię zapasową stosu i skopiować stos innego wątku do pamięci podczas przełączania wątków)? Ile czasu to faktycznie oszczędza? Czy to coś więcej?

Jankes
źródło
5
krótka odpowiedź: chodzi bardziej o narzut związany z posiadaniem wątku na połączenie. nieblokujący io pozwala uniknąć wątku na połączenie.
Dan D.
10
Blokowanie operacji we / wy jest kosztowne w systemie, w którym nie można utworzyć tylu wątków, ile istnieje połączeń. Na JVM możesz utworzyć kilka tysięcy wątków, ale co, jeśli masz ponad 100 000 połączeń? Musisz więc trzymać się rozwiązania asynchronicznego. Są jednak języki, w których wątki nie są drogie (np. Zielone nici), jak w Go / Erlang / Rust, gdzie 100 000 wątków nie stanowi problemu. Gdy liczba wątków może być duża, uważam, że blokowanie operacji we / wy zapewnia szybsze czasy odpowiedzi. Ale to jest coś, o co musiałbym również zapytać ekspertów, czy tak jest w rzeczywistości.
OlliP
@OliverPlow, też tak myślę, ponieważ blokowanie operacji we / wy zwykle oznacza, że ​​pozwalamy systemowi obsługiwać „zarządzanie równoległe”, zamiast robić to samodzielnie, korzystając z kolejek zadań i tym podobnych.
Pacerier
1
@DanD., A co, jeśli narzut związany z posiadaniem wątków jest równy narzutowi związanemu z wykonywaniem nieblokujących operacji we / wy? (zwykle prawdziwe w przypadku zielonych nitek)
Pacerier
„kopiowanie stosu” nie następuje. Różne wątki mają swoje stosy pod różnymi adresami. Każdy wątek ma swój własny wskaźnik stosu wraz z innymi rejestrami. Przełącznik kontekstu zapisuje / przywraca tylko stan architektury (w tym wszystkie rejestry), ale nie pamięć. Pomiędzy wątkami w tym samym procesie jądro nie musi nawet zmieniać tabel stron.
Peter Cordes

Odpowiedzi:

44

Największą zaletą nieblokujących lub asynchronicznych operacji we / wy jest to, że wątek może równolegle kontynuować swoją pracę. Oczywiście można to osiągnąć również za pomocą dodatkowego gwintu. Jak powiedziałeś, aby uzyskać najlepszą ogólną wydajność (systemu), wydaje mi się, że lepiej byłoby używać asynchronicznych operacji we / wy, a nie wielu wątków (co zmniejsza przełączanie wątków).

Przyjrzyjmy się możliwym implementacjom programu serwera sieciowego, który powinien obsłużyć 1000 klientów połączonych równolegle:

  1. Jeden wątek na połączenie (może blokować we / wy, ale może też być nieblokującym we / wy).
    Każdy wątek wymaga zasobów pamięci (także pamięci jądra!), Co jest wadą. Każdy dodatkowy wątek oznacza więcej pracy dla planisty.
  2. Jeden gwint na wszystkie połączenia.
    To powoduje obciążenie systemu, ponieważ mamy mniej wątków. Ale uniemożliwia to również wykorzystanie pełnej wydajności komputera, ponieważ możesz skończyć napędzając jeden procesor do 100% i pozwalając wszystkim innym procesorom pracować bezczynnie.
  3. Kilka wątków, w których każdy wątek obsługuje niektóre połączenia.
    To powoduje obciążenie systemu, ponieważ jest mniej wątków. Może korzystać ze wszystkich dostępnych procesorów. W systemie Windows to podejście jest obsługiwane przez interfejs API puli wątków .

Oczywiście posiadanie większej liczby wątków samo w sobie nie stanowi problemu. Jak pewnie zauważyłeś, wybrałem dość dużą liczbę połączeń / wątków. Wątpię, czy zauważysz jakąkolwiek różnicę między trzema możliwymi implementacjami, jeśli mówimy o zaledwie kilkunastu wątkach (to również sugeruje Raymond Chen w poście na blogu MSDN Czy system Windows ma limit 2000 wątków na proces? ).

W systemie Windows użycie niebuforowanego wejścia / wyjścia pliku oznacza, że ​​wielkość zapisów musi być wielokrotnością rozmiaru strony. Nie testowałem tego, ale wygląda na to, że może to również wpłynąć pozytywnie na wydajność zapisu dla buforowanych zapisów synchronicznych i asynchronicznych.

Kroki od 1 do 7, które opisujesz, dają dobre wyobrażenie o tym, jak to działa. W systemie Windows system operacyjny poinformuje Cię o zakończeniu asynchronicznego I / O ( WriteFileze OVERLAPPEDstrukturą) za pomocą zdarzenia lub wywołania zwrotnego. Funkcje oddzwaniania będą wywoływane tylko na przykład wtedy, gdy Twój kod wywoła WaitForMultipleObjectsExz bAlertableustawioną na true.

Więcej lektur w sieci:

Werner Henze
źródło
Z punktu widzenia sieci powszechna wiedza (Internet, komentarze ekspertów) sugeruje, że znaczne zwiększenie max. liczba wątków żądań to zła rzecz w blokowaniu operacji we / wy (jeszcze wolniejsze przetwarzanie żądań) ze względu na wzrost pamięci i czas przełączania kontekstu, ale czy Async IO nie robi tego samego, gdy odracza zadanie do innego wątku? Tak, możesz teraz obsłużyć więcej żądań, ale w tle masz taką samą liczbę wątków. Jaka jest prawdziwa korzyść z tego?
JavierJ
1
@JavierJ Wydajesz się sądzić, że jeśli n wątków wykona plik asynchroniczny IO, zostanie utworzonych kolejnych n wątków, aby wykonać blokowanie pliku IO? To nie jest prawda. System operacyjny obsługuje operacje we / wy pliku asynchronicznego i nie ma potrzeby blokowania podczas oczekiwania na zakończenie operacji we / wy. Może kolejkować żądania IO i jeśli wystąpi przerwanie sprzętowe (np. DMA), może oznaczyć żądanie jako wykonane i ustawić zdarzenie, które sygnalizuje wątek wywołującego. Nawet jeśli wymagany byłby dodatkowy wątek, system operacyjny mógłby używać tego wątku do wielu żądań we / wy z wielu wątków.
Werner Henze
Dzięki, ma to sens, jeśli chodzi o obsługę IO pliku asynchronicznego systemu operacyjnego, ale kiedy piszę kod dla rzeczywistej implementacji tego (z punktu widzenia sieci), mówię, że w przypadku Java Servlet 3.0 NIO nadal widzę wątek dla żądania i wątek w tle ( async) zapętlanie w celu odczytania pliku, bazy danych lub cokolwiek innego.
JavierJ
1
@piyushGoyal I recote my answer. Mam nadzieję, że teraz jest to jaśniejsze.
Werner Henze
1
W systemie Windows użycie asynchronicznego wejścia / wyjścia pliku oznacza, że ​​wielkość zapisów musi być wielokrotnością rozmiaru strony. - nie, nie działa. Myślisz o niebuforowanym I / O. (Często są używane razem, ale nie muszą)
Harry Johnston
29

We / wy obejmuje wiele rodzajów operacji, takich jak odczytywanie i zapisywanie danych z dysków twardych, uzyskiwanie dostępu do zasobów sieciowych, wywoływanie usług internetowych lub pobieranie danych z baz danych. W zależności od platformy i rodzaju operacji, asynchroniczne operacje we / wy zwykle korzystają z dowolnego sprzętu lub niskopoziomowego wsparcia systemowego do wykonywania operacji. Oznacza to, że będzie to wykonywane z jak najmniejszym wpływem na procesor.

Na poziomie aplikacji asynchroniczne operacje we / wy uniemożliwiają wątkom czekanie na zakończenie operacji we / wy. Zaraz po uruchomieniu asynchronicznej operacji we / wy zwalnia wątek, w którym została uruchomiona, i rejestrowane jest wywołanie zwrotne. Po zakończeniu operacji wywołanie zwrotne jest umieszczane w kolejce do wykonania w pierwszym dostępnym wątku.

Jeśli operacja we / wy jest wykonywana synchronicznie, jej działający wątek nie robi nic do momentu zakończenia operacji. Środowisko wykonawcze nie wie, kiedy zakończy się operacja we / wy, więc będzie okresowo dostarczać trochę czasu procesora do oczekującego wątku, czyli czasu procesora, który w przeciwnym razie mógłby zostać wykorzystany przez inne wątki, które mają do wykonania rzeczywiste operacje związane z procesorem.

Tak więc, jak wspomniał @ user1629468, asynchroniczne operacje we / wy nie zapewniają lepszej wydajności, ale raczej lepszą skalowalność. Jest to oczywiste, gdy działa w kontekstach, które mają ograniczoną liczbę dostępnych wątków, tak jak ma to miejsce w przypadku aplikacji internetowych. Aplikacje internetowe zwykle używają puli wątków, z której przypisują wątki do każdego żądania. Jeśli żądania są blokowane podczas długotrwałych operacji we / wy, istnieje ryzyko wyczerpania puli internetowej i spowodowania zawieszenia lub spowolnienia reakcji aplikacji internetowej.

Jedną z rzeczy, które zauważyłem, jest to, że asynchroniczne operacje we / wy nie są najlepszą opcją w przypadku bardzo szybkich operacji we / wy. W takim przypadku korzyść z nie utrzymywania zajętości wątku podczas oczekiwania na zakończenie operacji we / wy nie jest bardzo ważna, a fakt, że operacja jest uruchamiana w jednym wątku, a kończy się w innym, zwiększa ogólne wykonanie.

Możesz przeczytać bardziej szczegółowe badania, które ostatnio przeprowadziłem na temat asynchronicznych operacji we / wy i wielowątkowości tutaj .

Florin Dumitrescu
źródło
Zastanawiam się, czy warto byłoby dokonać rozróżnienia między operacjami we / wy, które mają się zakończyć, a rzeczami, które mogą nie [np. „Uzyskać następny znak, który dotrze do portu szeregowego”, w przypadkach, gdy zdalne urządzenie może lub nie wyślij cokolwiek]. Jeśli oczekuje się, że operacja we / wy zakończy się w rozsądnym czasie, można opóźnić czyszczenie powiązanych zasobów do czasu zakończenia operacji. Gdyby jednak operacja nigdy się nie zakończyła, takie opóźnienie byłoby nieuzasadnione.
supercat
@supercat scenariusz, który opisujesz, jest używany w aplikacjach i bibliotekach niższego poziomu. Serwery na nim polegają, ponieważ stale czekają na połączenia przychodzące. Async we / wy, jak opisano powyżej, nie pasuje do tego scenariusza, ponieważ opiera się na uruchomieniu określonej operacji i zarejestrowaniu wywołania zwrotnego w celu jej zakończenia. W przypadku, gdy opisujesz, musisz zarejestrować wywołanie zwrotne na zdarzenie systemowe i przetwarzać każde powiadomienie. Ciągle przetwarzasz dane wejściowe, zamiast wykonywać operacje. Jak powiedziano, zwykle odbywa się to na niskim poziomie, prawie nigdy w twoich aplikacjach.
Florin Dumitrescu
Wzorzec jest dość powszechny w aplikacjach, które są dostarczane z różnymi typami sprzętu. Porty szeregowe nie są tak powszechne jak kiedyś, ale chipy USB, które emulują porty szeregowe, są dość popularne w projektowaniu specjalistycznego sprzętu. Znaki z takich rzeczy obsługiwane są na poziomie aplikacji, ponieważ system operacyjny nie będzie miał możliwości zorientowania się, że ciąg wprowadzonych znaków oznacza, że ​​np. Szuflada kasowa została otwarta i gdzieś powinno zostać wysłane powiadomienie.
supercat
Nie sądzę, aby część dotycząca kosztu procesora w zakresie blokowania operacji we / wy była dokładna: w stanie blokowania wątek, który wyzwolił blokowanie operacji we / wy, jest czekany przez system operacyjny i nie kosztuje procesora do momentu pełnego zakończenia operacji we / wy, po czym czy system operacyjny (powiadamia przerwaniami) wznawia zablokowany wątek. To, co opisałeś (zajęte oczekiwanie przez długie odpytywanie) nie jest sposobem implementacji blokowania IO w prawie każdym środowisku wykonawczym / kompilatorze.
Lifu Huang
4

Głównym powodem korzystania z AIO jest skalowalność. Z perspektywy kilku wątków korzyści nie są oczywiste. Ale gdy system skaluje się do 1000 wątków, AIO zapewni znacznie lepszą wydajność. Zastrzeżenie jest takie, że biblioteka AIO nie powinna wprowadzać dalszych wąskich gardeł.

fissurezone
źródło
4

Aby założyć poprawę szybkości ze względu na jakąkolwiek formę przetwarzania wielu komputerów, należy założyć, że wiele zadań opartych na procesorach jest wykonywanych jednocześnie na wielu zasobach obliczeniowych (zazwyczaj rdzeniach procesora) lub że nie wszystkie zadania zależą od równoczesnego użycia ten sam zasób - to znaczy, że niektóre zadania mogą zależeć od jednego podkomponentu systemu (powiedzmy pamięć dyskowa), podczas gdy niektóre zadania zależą od innego (odbieranie komunikacji z urządzenia peryferyjnego), a jeszcze inne mogą wymagać użycia rdzeni procesora.

Pierwszy scenariusz jest często nazywany programowaniem „równoległym”. Drugi scenariusz jest często określany jako programowanie „współbieżne” lub „asynchroniczne”, chociaż termin „współbieżny” jest czasami używany również w odniesieniu do przypadku, gdy po prostu zezwala się systemowi operacyjnemu na przeplatanie wykonywania wielu zadań, niezależnie od tego, czy takie wykonanie musi zająć umieszczać szeregowo lub jeśli można użyć wielu zasobów, aby uzyskać wykonywanie równoległe. W tym drugim przypadku określenie „współbieżne” odnosi się ogólnie do sposobu, w jaki wykonanie jest zapisywane w programie, a nie z perspektywy faktycznej jednoczesności wykonywania zadań.

Bardzo łatwo o tym wszystkim mówić z milczącymi założeniami. Na przykład niektórzy szybko zgłaszają oświadczenie, takie jak „Asynchroniczne operacje we / wy będą szybsze niż wielowątkowe we / wy”. To twierdzenie jest wątpliwe z kilku powodów. Po pierwsze, może się zdarzyć, że pewna podana asynchroniczna struktura we / wy jest zaimplementowana dokładnie z wielowątkowością, w którym to przypadku są one tym samym i nie ma sensu mówić, że jedna koncepcja „jest szybsza” niż druga .

Po drugie, nawet w przypadku, gdy istnieje jednowątkowa implementacja struktury asynchronicznej (np. Jednowątkowa pętla zdarzeń), nadal należy przyjąć założenie dotyczące tego, co robi ta pętla. Na przykład jedną głupią rzeczą, jaką można zrobić z jednowątkową pętlą zdarzeń, jest żądanie asynchronicznego wykonania dwóch różnych zadań związanych wyłącznie z procesorem. Jeśli zrobiłeś to na maszynie z wyidealizowanym rdzeniem pojedynczego procesora (ignorując współczesne optymalizacje sprzętowe), wykonanie tego zadania „asynchronicznie” nie byłoby tak naprawdę inne niż wykonanie go z dwoma niezależnie zarządzanymi wątkami lub tylko z jednym samotnym procesem - - różnica może sprowadzać się do przełączania kontekstu wątków lub optymalizacji harmonogramu systemu operacyjnego, ale jeśli oba zadania są kierowane do procesora, w obu przypadkach byłoby podobnie.

Warto wyobrazić sobie wiele nietypowych lub głupich przypadków narożnych, na które możesz się natknąć.

„Asynchroniczny” nie musi być współbieżny, na przykład tak jak powyżej: „asynchronicznie” wykonujesz dwa zadania związane z procesorem na maszynie z dokładnie jednym rdzeniem procesora.

Wykonywanie wielowątkowe nie musi być współbieżne: tworzysz dwa wątki na maszynie z jednym rdzeniem procesora lub prosisz dwa wątki o pozyskanie innego rodzaju rzadkiego zasobu (wyobraź sobie, na przykład, sieciową bazę danych, która może ustanowić tylko jeden połączenie na raz). Wykonywanie wątków może być przeplatane, jednak harmonogram systemu operacyjnego uzna to za stosowne, ale ich całkowity czas wykonywania nie może zostać zmniejszony (i zostanie zwiększony z przełączania kontekstu wątku) na jednym rdzeniu (lub bardziej ogólnie, jeśli spawnujesz więcej wątków niż jest) rdzeni, aby je uruchomić lub mieć więcej wątków proszących o zasób, niż to, co zasób może utrzymać). To samo dotyczy również przetwarzania wielokrotnego.

Zatem ani asynchroniczne operacje we / wy, ani wielowątkowość nie muszą oferować żadnego wzrostu wydajności pod względem czasu wykonywania. Mogą nawet spowolnić działanie.

Jeśli jednak zdefiniujesz konkretny przypadek użycia, taki jak określony program, który zarówno wykonuje wywołanie sieciowe w celu pobrania danych z zasobu podłączonego do sieci, takiego jak zdalna baza danych, a także wykonuje pewne lokalne obliczenia związane z procesorem, możesz zacząć rozważać różnice w wydajności między tymi dwiema metodami przy określonym założeniu dotyczącym sprzętu.

Pytania, które należy zadać: Ile kroków obliczeniowych muszę wykonać i ile jest niezależnych systemów zasobów, aby je wykonać? Czy istnieją podzbiory kroków obliczeniowych, które wymagają użycia niezależnych podskładników systemu i mogą przynosić korzyści z robienia tego jednocześnie? Ile mam rdzeni procesorów i jakie są narzuty związane z używaniem wielu procesorów lub wątków do wykonywania zadań na oddzielnych rdzeniach?

Jeśli Twoje zadania w dużej mierze opierają się na niezależnych podsystemach, rozwiązanie asynchroniczne może być dobre. Jeśli liczba wątków potrzebnych do jego obsługi byłaby duża, tak że przełączanie kontekstu stało się nietrywialne dla systemu operacyjnego, wówczas rozwiązanie asynchroniczne jednowątkowe mogłoby być lepsze.

Ilekroć zadania są powiązane przez ten sam zasób (np. Wiele potrzeb, aby jednocześnie uzyskać dostęp do tej samej sieci lub zasobu lokalnego), wówczas wielowątkowość prawdopodobnie wprowadzi niezadowalający narzut, a podczas gdy asynchronia jednowątkowa może wprowadzić mniejszy narzut, w takim zasobie ograniczona sytuacja to również nie może spowodować przyspieszenia. W takim przypadku jedyną opcją (jeśli chcesz przyspieszyć) jest udostępnienie wielu kopii tego zasobu (np. Wielu rdzeni procesora, jeśli rzadkim zasobem jest procesor; lepsza baza danych, która obsługuje więcej równoczesnych połączeń, jeśli rzadki zasób to baza danych z ograniczeniem połączeń itp.).

Można to ująć w inny sposób: zezwolenie systemowi operacyjnemu na przeplatanie wykorzystania jednego zasobu do dwóch zadań nie może być szybsze niż zwykłe pozwolenie jednemu zadaniu na wykorzystanie zasobu, podczas gdy drugie czeka, a następnie pozwolenie drugiemu zadaniu na kolejne zakończenie. Co więcej, koszt planistycznego przeplatania oznacza, że ​​w każdej rzeczywistej sytuacji faktycznie powoduje spowolnienie. Nie ma znaczenia, czy użycie z przeplotem dotyczy procesora, zasobu sieciowego, zasobu pamięci, urządzenia peryferyjnego czy jakiegokolwiek innego zasobu systemowego.

ely
źródło
2

Jedną z możliwych implementacji nieblokującego wejścia / wyjścia jest dokładnie to, co powiedziałeś, z pulą wątków działających w tle, które blokują wejścia / wyjścia i powiadamiają wątek inicjatora wejścia / wyjścia za pośrednictwem mechanizmu wywołania zwrotnego. W rzeczywistości tak działa moduł AIO w glibc. Oto kilka niejasnych szczegółów dotyczących implementacji.

Chociaż jest to dobre rozwiązanie, które jest dość przenośne (o ile masz wątki), system operacyjny zazwyczaj jest w stanie wydajniej obsługiwać nieblokujące operacje we / wy. W tym artykule w Wikipedii wymieniono możliwe implementacje poza pulą wątków.

Miguel
źródło
2

Obecnie jestem w trakcie wdrażania async io na platformie embedded przy użyciu protothreads. Nieblokujący io decyduje o różnicy między pracą z prędkością 16000 kl./s a 160 kl./s. Największą zaletą nieblokującego io jest to, że możesz tak zorganizować swój kod, aby robić inne rzeczy, podczas gdy sprzęt robi swoje. Nawet inicjalizację urządzeń można przeprowadzić równolegle.

Jaskółka oknówka

user2826084
źródło
1

W Node uruchamianych jest wiele wątków, ale jest to warstwa w dół w czasie wykonywania C ++.

„Więc tak, NodeJS jest jednowątkowy, ale to połowa prawdy, w rzeczywistości jest sterowany zdarzeniami i jednowątkowy z procesami roboczymi w tle. Główna pętla zdarzeń jest jednowątkowa, ale większość operacji we / wy działa w osobnych wątkach, ponieważ interfejsy API I / O w Node.js są asynchroniczne / nieblokujące z założenia, aby dostosować się do pętli zdarzeń. "

https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea

„Node.js nie blokuje, co oznacza, że ​​wszystkie funkcje (wywołania zwrotne) są delegowane do pętli zdarzeń i są (lub mogą być) wykonywane przez różne wątki. Jest to obsługiwane przez środowisko wykonawcze Node.js.”

https://itnext.io/multi-threading-and-multi-process-in-node-js-ffa5bb5cde98 

Wyjaśnienie „Węzeł jest szybszy, ponieważ nie blokuje…” jest trochę marketingowe i to jest świetne pytanie. Jest wydajna i skalowalna, ale nie jest dokładnie jednowątkowa.

SmokestackLightning
źródło
0

O ile wiem, poprawa polega na tym, że asynchroniczne we / wy wykorzystuje (mówię o MS System, tylko dla wyjaśnienia) tak zwane porty zakończenia we / wy . Korzystając z wywołania asynchronicznego, platforma automatycznie wykorzystuje taką architekturę, co ma być znacznie bardziej wydajne niż standardowy mechanizm wątkowania. Z własnego doświadczenia mogę powiedzieć, że rozsądnie czułbyś, że twoja aplikacja jest bardziej reaktywna, jeśli wolisz AsyncCalls zamiast blokowania wątków.

Felice Pollano
źródło
0

Pozwólcie, że podam wam kontrprzykład, że asynchroniczne we / wy nie działa. Piszę proxy podobne do poniższego przy użyciu boost :: asio. https://github.com/ArashPartow/proxy/blob/master/tcpproxy_server.cpp

Jednak scenariusz mojego przypadku jest taki, że wiadomości przychodzące (od strony klienta) są szybkie, podczas gdy wychodzące (po stronie serwera) są powolne dla jednej sesji, aby nadążyć za prędkością przychodzącą lub zmaksymalizować całkowitą przepustowość proxy, musimy użyć wiele sesji w ramach jednego połączenia.

Tak więc ta asynchroniczna struktura we / wy już nie działa. Potrzebujemy puli wątków do wysłania na serwer, przypisując każdemu wątkowi sesję.

Zhidian Du
źródło