Jeśli masz kod po stronie serwera (np. Niektóre ApiController
), a twoje funkcje są asynchroniczne - więc zwracają się Task<SomeObject>
- czy jest najlepszą praktyką, że za każdym razem czekasz na funkcje, które wywołujesz ConfigureAwait(false)
?
Przeczytałem, że jest bardziej wydajny, ponieważ nie musi przełączać kontekstów wątków z powrotem do oryginalnego kontekstu wątków. Jednak w przypadku interfejsu ASP.NET Web Api, jeśli twoje zapytanie przychodzi do jednego wątku, a ty czekasz na jakąś funkcję i wywołanie, ConfigureAwait(false)
które potencjalnie mogą umieścić cię w innym wątku, gdy zwracasz końcowy wynik działania tej ApiController
funkcji.
Poniżej podałem przykład tego, o czym mówię:
public class CustomerController : ApiController
{
public async Task<Customer> Get(int id)
{
// you are on a particular thread here
var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false);
// now you are on a different thread! will that cause problems?
return customer;
}
}
źródło
HttpContext.Current
przepływa przez ASP.NETSynchronizationContext
, który przepływa domyślnie, gdy Tyawait
, ale nie przepływa przezContinueWith
. OTOH, kontekst wykonania (w tym ograniczenia bezpieczeństwa) to kontekst wymieniony w CLR za pośrednictwem C #, i jest przepuszczany przez obaContinueWith
iawait
(nawet jeśli używaszConfigureAwait(false)
).ConfigureAwait
rzeczywistości ma on sens tylko wtedy , gdy czekasz na zadanie , podczas gdyawait
działa na „oczekiwane”. Inne rozważane opcje to: Czy domyślne zachowanie powinno odrzucić kontekst, jeśli znajduje się w bibliotece? A może masz ustawienia kompilatora dla domyślnego zachowania kontekstowego? Oba zostały odrzucone, ponieważ trudniej jest po prostu przeczytać kod i powiedzieć, co robi.ConfigureAwait(false)
unikać zakleszczenia opartego naResult
/Wait
, ponieważ w ASP.NET nie powinieneś używaćResult
/Wait
.Krótka odpowiedź na twoje pytanie: Nie. Nie powinieneś dzwonić
ConfigureAwait(false)
na takim poziomie aplikacji.Długa wersja TL; DR długiej odpowiedzi: jeśli piszesz bibliotekę, w której nie znasz konsumenta i nie potrzebujesz kontekstu synchronizacji (którego, jak sądzę, nie powinieneś mieć w bibliotece), zawsze powinieneś jej używać
ConfigureAwait(false)
. W przeciwnym razie konsumenci Twojej biblioteki mogą napotkać impas, wykorzystując metody asynchroniczne w sposób blokujący. To zależy od sytuacji.Oto nieco bardziej szczegółowe wyjaśnienie znaczenia
ConfigureAwait
metody (cytat z mojego posta na blogu):Oto dwa świetne artykuły, które są dokładnie na twoje pytanie:
Na koniec jest świetny krótki film od Luciana Wischika na ten temat: Metody bibliotek asynchronicznych powinny rozważyć użycie Task.ConfigureAwait (false) .
Mam nadzieję że to pomoże.
źródło
Task
chodzi o stos, aby uzyskaćSynchronizationContext
, co jest złe. JestSynchronizationContext
on pobierany przed wywołaniem,Task
a następnie reszta kodu jest kontynuowana,SynchronizationContext
jeśli ifSynchronizationContext.Current
nie jest zerowy.SynchronizationContext.Current
jest ona czysta / lub że biblioteka jest wywoływana w miejsceTask.Run()
zamiast konieczności pisania.ConfigureAwait(false)
całej biblioteki klas?.ConfigureAwait(false)
. Być może byłoby to łatwiejsze dla autorów bibliotek, gdyby takie było zachowanie domyślne, ale przypuszczam, że nieco trudniejsze jest poprawne napisanie biblioteki jest lepsze niż utrudnienie prawidłowego napisania aplikacji.Największą wadą, jaką znalazłem przy użyciu ConfigureAwait (false) jest to, że kultura wątków jest przywracana do wartości domyślnych systemu. Jeśli skonfigurowałeś kulturę np. ...
i hostujesz na serwerze, którego kultura jest ustawiona na en-US, to przekonasz się, że ConfigureAwait (false) nazywa się CultureInfo. CurrentCulture zwróci en-AU i po uzyskaniu en-US. to znaczy
Jeśli twoja aplikacja robi coś, co wymaga specyficznego dla kultury formatowania danych, musisz o tym pamiętać podczas korzystania z ConfigureAwait (false).
źródło
ConfigureAwait(false)
są używane.Mam kilka ogólnych przemyśleń na temat wdrożenia
Task
:using
.ConfigureAwait
został wprowadzony w 4.5.Task
został wprowadzony w wersji 4.0.Task.ContinueWith
nie mają b / c okazało się, że zmiana kontekstu jest droga i domyślnie jest wyłączona.Mam kilka postów na ten temat, ale moje zdanie - oprócz ładnej odpowiedzi Tugberka - polega na tym, że powinieneś włączyć wszystkie interfejsy API asynchronicznie i idealnie przepłynąć kontekst. Ponieważ wykonujesz asynchronię, możesz po prostu użyć kontynuacji zamiast czekać, więc nie nastąpi zakleszczenie, ponieważ w bibliotece nie zostanie wykonane żadne czekanie, a przepływ zostanie zachowany, więc kontekst zostanie zachowany (na przykład HttpContext).
Problem polega na tym, że biblioteka udostępnia synchroniczny interfejs API, ale korzysta z innego asynchronicznego interfejsu API - dlatego należy użyć
Wait()
/Result
w kodzie.źródło
Task.Dispose
jeśli chcesz; po prostu nie potrzebujesz przez większość czasu. 2)Task
został wprowadzony w .NET 4.0 jako część TPL, co nie wymagałoConfigureAwait
; kiedyasync
został dodany, ponownie wykorzystali istniejącyTask
typ zamiast wynaleźć nowyFuture
.Task
s; „kontekst” kontrolowany przezContinueWith
toSynchronizationContext
lubTaskScheduler
. Te różne konteksty zostały szczegółowo wyjaśnione na blogu Stephena Touba .Thread
s, ale już nie z nimContinueWith()
), to sprawia, że twoja odpowiedź jest myląca z czytaniem.