Ostatnio wydaje mi się, że nie mam dość niesamowitego wzoru asynchronicznego oczekiwania w C # 5.0. Gdzie byłeś całe moje życie?
Jestem absolutnie podekscytowana prostą składnią, ale mam jedną małą trudność. Mój problem polega na tym, że funkcje asynchroniczne mają zupełnie inną deklarację niż funkcje zwykłe. Ponieważ tylko inne funkcje asynchroniczne mogą oczekiwać na inne funkcje asynchroniczne, kiedy próbuję przenieść asynchroniczny stary kod blokujący, mam efekt domina funkcji, które muszę przekonwertować.
Ludzie nazywają to infestacją zombie . Kiedy async ugryzie Cię w kodzie, będzie coraz większy. Proces przenoszenia nie jest trudny, po prostu wrzuca async
deklarację i zawija wartość zwracaną Task<>
. Ale denerwujące jest robienie tego w kółko podczas przenoszenia starego kodu synchronicznego.
Wydaje mi się, że byłoby znacznie bardziej naturalne, gdyby oba typy funkcji (asynchroniczna i zwykła stara synchronizacja) miały dokładnie tę samą składnię. W takim przypadku przeniesienie zajmie zero wysiłku, a ja bezboleśnie mogę przełączać się między tymi dwiema formami.
Myślę, że to może zadziałać, jeśli będziemy przestrzegać następujących zasad:
Funkcje asynchroniczne nie będą już wymagać
async
deklaracji. Ich typy zwrotów nie musiałyby być zawijaneTask<>
. Kompilator sam zidentyfikuje funkcję asynchroniczną podczas kompilacji i automatycznie zapakuje zadanie <> w razie potrzeby.Nigdy więcej wywołań ognia i zapomnij o funkcjach asynchronicznych. Jeśli chcesz wywołać funkcję asynchroniczną, musisz na nią poczekać. I tak prawie nie używam ognia i zapomnienia, a wszystkie przykłady szalonych wyścigów lub impasów zawsze wydają się na nich oparte. Myślę, że są one zbyt mylące i „poza dotykiem” z synchronicznym sposobem myślenia, który próbujemy wykorzystać.
Jeśli naprawdę nie możesz żyć bez ognia i zapomnienia, będzie to wymagało specjalnej składni. W każdym razie nie będzie to częścią prostej zunifikowanej składni, o której mówię.
Jedynym słowem kluczowym potrzebnym do oznaczenia połączenia asynchronicznego jest
await
. Jeśli czekasz, połączenie jest asynchroniczne. Jeśli tego nie zrobisz, połączenie jest po prostu stare synchroniczne (pamiętaj, że nie mamy już ognia i zapomnij).Kompilator automatycznie rozpozna funkcje asynchroniczne (ponieważ nie mają już specjalnej deklaracji). Zasada 4 sprawia, że jest to bardzo proste - jeśli funkcja zawiera
await
wywołanie, jest asynchroniczna.
Czy to może zadziałać? czy coś mi brakuje? Ta zunifikowana składnia jest znacznie bardziej płynna i może całkowicie rozwiązać problem zombie.
Kilka przykładów:
// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }
// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }
// now let's try all combinations and see what they do:
// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();
// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();
// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();
// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();
Uwaga: jest to kontynuacja interesującej powiązanej dyskusji w SO
źródło
await
razu. Możesz zrobić coś takiegovar task = FooAsync(); Bar(); await task;
. Jak mam to zrobić w twojej propozycji?async
. Myślę, że to jedna z wielkich zaletasync
-await
: pozwala łatwo komponować operacje asynchroniczne (i to nie tylko w najprostszy sposób „start A, czekanie na A, start B, czekanie na B”). I właśnie w tym celu istnieje specjalna składnia: nazywa sięawait
.Odpowiedzi:
Odpowiedź na twoje pytanie znajduje się już w łączącym się pytaniu SO.
Kiedy pojawiła się WinRT , projektanci opisywali, w jaki sposób zdecydowali, które operacje będą asynchroniczne. Zdecydowali, że wszystko, co zajmie 50 ms lub więcej, będzie asynchroniczne, a pozostała część metod będzie zwykłymi, niesynchronicznymi metodami.
Ile metod trzeba było przepisać, aby były asynchroniczne? Około 10 procent z nich. Pozostałe 90% w ogóle nie uległo zmianie.
Eric Lippert wyjaśnia dość szczegółowo szczegóły techniczne, dlaczego zdecydowali się nie stosować jednego rozwiązania dla wszystkich. On w zasadzie mówi, że
async
iawait
są częściowe wdrożenie kontynuację przechodząc stylu, a że optymalizacja takiego stylu pasują do wszystkich przypadków jest trudny problem.źródło
async
miałby wywoływać inwazję zombie. Nawet jeśli metoda wywołuje 10 innych metod, nie musisz po prostu przejść doasync
metody najwyższego poziomu?