async + czekać == synchronizacja?

24

Natknąłem się na ten post, który mówi o składaniu asynchronicznych żądań internetowych.

Odkładając na bok prostotę, jeśli w prawdziwym świecie wszystko, co robisz, to wykonujesz żądanie asynchroniczne i czekasz na nie w następnej linii, czy to nie to samo, co wykonywanie połączenia synchronicznego?

Mrchief
źródło
5
Nie dokładnie. Twój kod jest synchroniczny w tym sensie, że nic się nie dzieje, dopóki nie uzyskasz wyniku. Jednak pod spodem prawdopodobnie zrezygnowałeś z wątku, na którym działałeś, dopóki nie powróciła metoda asynchroniczna, a następnie przydzielono ci inny wątek, aby kontynuować wykonywanie.
R0MANARMY
2
jest tak, ale za pomocą asynchronizacji można wykonać kolejną asynchronię w tym samym czasie, a następnie poczekać na 2, z synchronizacją nie jest to możliwe
maniak ratchet
Oto artykuł ( tomasp.net/blog/async-compilation-internals.aspx ) omawiający niektóre z podstawowych zagadnień związanych z asynchronią w C # - jest to część serii obejmującej programowanie asynchroniczne w C # i F #.
Paul
@ratchetfreak: Tak, to oczywiste, jeśli wykonujesz wiele połączeń.
Mrchief,
@ R0MANARMY: Jeśli twoja aplikacja robi inne rzeczy, to włącza się tak i async + czekaj. Akim mówi to najlepiej! Ale wyobraź sobie, że kod nie znajduje się w module obsługi button_click ani w żadnym innym takim programie obsługi zdarzeń. Jeśli ktoś ślepo skopiuje kod (asynchronicznie + oczekuj wierszy), na dowolną metodę, może to prowadzić do fałszywego wrażenia, że ​​kod jest asynchroniczny, ale w rzeczywistości może nie być.
Mrchief,

Odpowiedzi:

32

Nie, z async + await != syncpowodu kontynuacji

Z MSDN „Programowanie asynchroniczne za pomocą Async i Await (C # i Visual Basic)”

Metody asynchroniczne mają być operacjami nieblokującymi. Wyrażenie oczekujące w metodzie asynchronicznej nie blokuje bieżącego wątku, gdy oczekiwane zadanie jest uruchomione. Zamiast tego wyrażenie podpisuje resztę metody jako kontynuację i zwraca kontrolę do obiektu wywołującego metodę asynchroniczną .

Na przykład wykonanie asynchroniczne nie blokuje wątku interfejsu użytkownika i Some TextBox.Textzostanie zaktualizowane po zakończeniu pobierania

private async void OnButtonClick()
{
   SomeTextBox.Text = await new WebClient().DownloadStringTaskAsync("http://stackoverflow.com/");
}
Akim
źródło
Bardzo ładnie powiedziane!
Mrchief,
Czy możesz wyjaśnić bardziej szczegółowo. Czy mówisz ... że bez tego ... nie byłbyś w stanie wchodzić w interakcje z interfejsem użytkownika ... tak jak byłoby to w głównym wątku. Czy to oznacza, że ​​będzie to miało zastosowanie tylko w programie typu aplikacja umieszczonym w sieci, w którym interakcja jest niezależna od wątku serwera WWW. Tak więc w łupinie nakrętki staje się to ważne, tzn. Nie synchronizuje się *, gdy główny wątek jest działającym wątkiem. Czy nie stworzyłoby to nieoczekiwanego zachowania, tj. W aplikacji (1 wątek główny) kliknięto dwa przyciski… ale powinieneś być w stanie kliknąć 1 bez pierwszego ukończenia?
Seabizkit
Co Console.WriteLine(await GetStringOverNetwork());? Co jeśli potrzebujesz wyniku asynchronicznego wywołania? Czy program blokuje się przy pierwszym dostępie, nawet jeśli wątek mógłby potencjalnie kontynuować wykonywanie?
Andrew
6

Nie, to nie to samo.

Twój asyncblok kodu czeka naawait wezwanie do powrotu, aby kontynuować, jednak reszta aplikacji nie czeka i może nadal jak zwykle.

W przeciwieństwie do tego, wywołanie synchroniczne sprawi, że cała aplikacja lub wątek zaczekają, aż kod zakończy się, aby kontynuować działanie z czymkolwiek innym.

Rachel
źródło
nie można zaimplementować połączenia synchronicznego jako async + czekać?
maniak zapadkowy
@ratchetfreak Wydaje mi się, że konfiguracja funkcji Oczekiwanie / asynchronizacja wiąże się z pewnym nakładem, więc nie sądzę, abyś chciał za jej pomocą kodować całą aplikację. Używam go tylko do wykonywania potencjalnie długo działających bloków kodu, aby nie blokować moich aplikacji. :)
Rachel
5

Pozwól, że wyjaśnię rzeczy dotyczące asynchronizacji / oczekiwania.

Po napotkaniu oczekiwania na maszynę stanową można natychmiast zwrócić kontrolę. Następnie, gdy oczekiwane połączenie zostanie zakończone, maszyna stanu bazowa umożliwia wznowienie wykonywania na linii po oczekiwaniu na połączenie.

Dlatego blok asynchroniczny nie jest blokowany ani nie czeka na zakończenie oczekiwanego połączenia; Kontrola jest zwracana natychmiast po napotkaniu polecenia oczekującego.

Podstawowa maszyna stanu jest częścią „magii” związanej z używaniem asynchronizacji / czekania, która nie jest nieużywana i pomijana.

Cedric Harris
źródło
2

Natknąłem się na to z myślą o tym samym pytaniu, ale po przeczytaniu odpowiedzi pytanie wydaje się pozostawać, pomieszane przez odniesienia do „magii pod maską”.

Z wyżej wspomnianego programowania asynchronicznego :

  • asyncKluczowe okazuje metody do metody asynchronicznej, co pozwala na użycie awaitsłowa kluczowego w jego organizmie.
  • Po zastosowaniu awaitsłowa kluczowego zawiesza metodę wywoływania i przywraca kontrolę nad swoim rozmówcą, dopóki oczekiwane zadanie nie zostanie zakończone.
  • awaitmoże być użyte tylko wewnątrz asyncmetody.

Czy kontekst, który napotyka, awaitzostaje zablokowany?

  • Tak . Zasadniczo jest to lokalna bariera synchronizacji w celu utrzymania znanego stanu w kontekście wykonania; oprócz tego, że inne konteksty, jeśli takie istnieją, nie są łączone.

Czy reszta aplikacji blokuje się przy await?

  • To zależy od sposobu napisania aplikacji. Jeśli jest to seria zależnych awaitzadań ed uruchamianych sekwencyjnie w tym samym kontekście (patrz: Próbowanie zrozumienia pewnych zachowań asynchronicznych / oczekiwania )

    await asyncCall1();
    await asyncCall2();  // waits for asyncCall1() to complete

    w ten sposób każdy awaitblokowałby odrodzenie następnego.

    Z drugiej strony, te same zadania zależne uruchamiane równolegle byłyby wykonywane równolegle, a kontekst blokowałby się tylko w odpowiednio. await:

    Task<int> t1 = asyncCall1();
    Task<string> t2 = asyncCall2();  // runs in parallel with asyncCall1()
    int val = await t1;
    string str = await t2;  // waits for asyncCall1() to complete

    Zasadniczo awaitzwraca wykonanie do zewnętrznego kontekstu, z którego wywoływany jest bieżący kontekst. Jednakże, jeśli sam kontekst zewnętrzny oczekuje na prąd, to jest jak sekwencja awaits w tym samym kontekście.

Tak więc, aby czerpać asynckorzyści, należy zaprojektować aplikację do uruchamiania kilku równoległych kontekstów (interfejs użytkownika, klient danych itp.), A następnie awaitw jednym kontekście daje wykonanie do innych kontekstów, aby cała aplikacja nie blokowała się na jednostce await.

przesadzone
źródło