W tym artykule MSDN znajduje się następujący przykładowy kod (lekko zredagowany dla zwięzłości):
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
FindAsync
Sposób pobiera Department
przedmiotu jego identyfikatora, i zwraca Task<Department>
. Następnie dział jest natychmiast sprawdzany, aby sprawdzić, czy jest pusty. Jak rozumiem, zapytanie o wartość Zadania w ten sposób zablokuje wykonanie kodu, dopóki wartość z oczekiwanej metody nie zostanie zwrócona, co skutecznie spowoduje wywołanie synchroniczne.
Dlaczego miałbyś to robić? Czy nie byłoby łatwiej po prostu wywołać metodę synchroniczną Find(id)
, jeśli i tak zamierzasz natychmiast zablokować?
c#
.net
asp.net-mvc
async
Robert Harvey
źródło
źródło
... else return null;
Następnie musisz sprawdzić, czy metoda rzeczywiście znalazła żądany dział.Odpowiedzi:
Nie do końca.
Po wywołaniu
await db.Departments.FindAsync(id)
zadanie jest wysyłane, a bieżący wątek jest zwracany do puli w celu użycia przez inne operacje. Przepływ wykonania jest zablokowany (tak jak by to było niezależnie od użyciadepartment
zaraz po, jeśli dobrze rozumiem), ale sam wątek może być używany przez inne rzeczy podczas oczekiwania na zakończenie operacji poza maszyną (i sygnalizowane przez port zdarzenia lub zakończenia).Jeśli zadzwoniłeś,
d.Departments.Find(id)
wątek tam siedzi i czeka na odpowiedź, nawet jeśli większość przetwarzania jest wykonywana na DB.Podczas wiązania dysku skutecznie zwalniasz zasoby procesora.
źródło
await
zrobiłem, to znak na pozostałej części metody jako kontynuacja tego samego wątku (są wyjątki; niektóre metody asynchroniczne rozwijają swój własny wątek) lub znak naasync
metodzie jako kontynuacja tego samego wątku i pozwolenie na pozostały kod do wykonania (jak widać, nie jestem krystalicznie jasny, jakasync
działa). To, co opisujesz, brzmi bardziej jak wyrafinowana formaThread.Sleep(untilReturnValueAvailable)
ConfigureAwait
iirc).await
do niego zadzwonićpublic async Task<ActionResult> Details(int? id)
. W przeciwnym razie oryginalne połączenie zostanie po prostu zablokowane, czekając nadepartment == null
rozwiązanie.await ...
„powrotu”FindAsync
połączenie zostało zakończone. To właśnie czeka. Nazywa się to czekaniem, ponieważ powoduje, że kod czeka na różne rzeczy. (Zauważ jednak, że to nie to samo, co sprawienie, że bieżący wątek zaczeka na rzeczy)Naprawdę nienawidzę tego, że żaden z przykładów nie pokazuje, jak można poczekać kilka linijek przed oczekiwaniem na zadanie. Rozważ to.
Ten rodzaj kodu zachęca przykłady i masz rację. To nie ma sensu. Uwalnia główny wątek do robienia innych rzeczy, takich jak reagowanie na dane wejściowe w interfejsie użytkownika, ale prawdziwą mocą asynchronizacji / oczekiwania jest to, że mogę łatwo robić inne rzeczy, czekając na zakończenie potencjalnie długiego zadania. Powyższy kod „zablokuje się” i zaczeka na wykonanie wiersza wydruku, aż otrzymamy Foo & Bar. Nie trzeba jednak czekać. Możemy to przetworzyć, czekając.
Teraz, dzięki przepisanemu kodowi, nie zatrzymujemy się i nie czekamy na nasze wartości, dopóki nie będziemy musieli. Zawsze szukam tego rodzaju możliwości. Sprytne określenie, kiedy czekamy, może doprowadzić do znacznej poprawy wydajności. W dzisiejszych czasach mamy wiele rdzeni, równie dobrze moglibyśmy z nich korzystać.
źródło
await
i pozwalasz wątkowi robić zupełnie inne rzeczy.Więc dzieje się więcej za kulisami. Async / Await to cukier składniowy. Najpierw spójrz na podpis funkcji FindAsync. Zwraca zadanie. Już widzisz magię słowa kluczowego, to rozpakowuje to Zadanie w Departamencie.
Funkcja wywołania nie blokuje się. To, co się dzieje, polega na tym, że przypisanie do działu i wszystko, co następuje po słowach kluczowych oczekujących, jest zamykane i dla wszystkich zamiarów i celów przekazywane do metody Task.ContinueWith (funkcja FindAsync jest automatycznie wykonywana w innym wątku).
Oczywiście dzieje się więcej za kulisami, ponieważ operacja jest przenoszona z powrotem do oryginalnego wątku (więc nie musisz się już martwić synchronizacją z interfejsem użytkownika podczas wykonywania operacji w tle) oraz w przypadku funkcji wywołującej Async ( i nazywany asynchronicznie) to samo dzieje się na stosie.
Tak więc dzieje się tak, gdy dostajesz magię operacji asynchronicznych bez pułapek.
źródło
Nie, nie wraca natychmiast. Oczekiwanie powoduje, że wywołanie metody jest asynchroniczne. Po wywołaniu FindAsync metoda Details zwraca zadanie, które nie zostało ukończone. Po zakończeniu FindAsync zwróci swój wynik do zmiennej departamentu i wznowi resztę metody Details.
źródło
async
await
generalnie nie tworzy nowych wątków, a nawet jeśli tak, nadal musisz poczekać, aż wartość tego działu ustali, czy jest ona pusta.public async Task<ActionResult>
należy równieżawait
ed.await
nie należy go mieszać z.Wait()
lub.Result
, ponieważ może to powodować zakleszczenia. Łańcuch asynchroniczny / oczekujący zwykle kończy się funkcją zasync void
sygnaturą, która jest najczęściej używana w procedurach obsługi zdarzeń lub w funkcjach wywoływanych bezpośrednio przez elementy interfejsu użytkownika.Lubię myśleć o „asynchronizacji” jak o umowie, która mówi: „mogę wykonać to asynchronicznie, jeśli zajdzie taka potrzeba, ale możesz do mnie zadzwonić jak każdą inną funkcję synchroniczną”.
Oznacza to, że jeden programista tworzył funkcje, a niektóre decyzje projektowe skłoniły ich do utworzenia / oznaczenia szeregu funkcji jako „asynchronicznych”. Osoba wywołująca / konsument funkcji może z nich korzystać zgodnie z decyzją. Jak mówisz, możesz albo zadzwonić, czekając tuż przed wywołaniem funkcji i poczekać na to, w ten sposób potraktowałeś to jak funkcję synchroniczną, ale jeśli chcesz, możesz zadzwonić bez oczekiwania jako
i po, powiedzmy, 10 liniach w dół funkcji, którą wywołujesz
stąd traktując to jako funkcję asynchroniczną.
To zależy od Ciebie.
źródło
„jeśli i tak chcesz natychmiast zablokować”, odpowiedź brzmi „Tak”. Tylko wtedy, gdy potrzebujesz szybkiej odpowiedzi, czekaj / asynchronizacja ma sens. Na przykład wątek interfejsu użytkownika przechodzi do metody naprawdę asynchronicznej, wątek interfejsu użytkownika powraca i kontynuuje nasłuchiwanie kliknięcia przycisku, podczas gdy kod poniżej „Oczekiwanie” zostanie wycięty przez inny wątek i ostatecznie uzyska wynik.
źródło