Najlepsza praktyka wywoływania ConfigureAwait dla całego kodu po stronie serwera
Jeśli masz kod po stronie serwera (tj. niektóre ApiController
) i twoje funkcje są asynchroniczne-więc zwracają Task<SomeObject>
- czy uważa się za najlepszą praktykę, że za każdym razem czekasz na funkcje, które wywołujesz ConfigureAwait(false)
?
Czytałem, że jest bardziej wydajny, ponieważ nie musi zmieniać kontekstu wątku z powrotem na oryginalny kontekst wątku. Jednak z ASP.NET Web Api, jeśli twoja prośba pojawia się w jednym wątku i czekasz na jakąś funkcję i wywołanie ConfigureAwait(false)
, które potencjalnie może umieścić cię na inny wątek, gdy zwracasz końcowy wynik swojej funkcji ApiController
.
Wpisałem poniżej 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;
}
}
4 answers
Aktualizacja: ASP.NET rdzeń nie posiada SynchronizationContext
. Jeśli jesteś na ASP.NET Core, nie ma znaczenia czy używasz ConfigureAwait(false)
czy nie.
Dla ASP.NET "pełny" lub "klasyczny" czy cokolwiek innego, reszta tej odpowiedzi nadal obowiązuje.
Original post (for non-Core ASP.NET):
This video by the ASP.NET zespół ma najlepsze informacje o używaniu async
na ASP.NET.
Czytałem, że jest bardziej wydajny, ponieważ nie trzeba przełączyć konteksty wątku z powrotem na oryginalny kontekst wątku.
Dotyczy to aplikacji UI, gdzie istnieje tylko jeden wątek UI, do którego musisz" zsynchronizować".
W ASP.NET, sytuacja jest nieco bardziej złożona. Gdy metoda async
wznawia wykonywanie, chwyta wątek z ASP.NET Pula wątków. Jeśli wyłączysz przechwytywanie kontekstu za pomocą ConfigureAwait(false)
, wtedy wątek po prostu kontynuuje wykonywanie metody bezpośrednio. Jeśli nie wyłączysz przechwytywania kontekstu, to wątek ponownie wprowadzi kontekst żądania, a następnie kontynuuje wykonywanie metody.
Więc ConfigureAwait(false)
nie zapisuje Ci wątku ASP.NET; zapisuje to ponowne wprowadzenie kontekstu żądania, ale zwykle jest to bardzo szybkie. ConfigureAwait(false)
może być użyteczne, jeśli próbujesz wykonać niewielką ilość równoległego przetwarzania żądania, ale tak naprawdę TPL lepiej pasuje do większości tych scenariuszy.
Właściwie, samo zrobienieJednak z ASP.NET Web Api, jeśli twoje żądanie przychodzi na jeden thread, i 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 funkcji ApiController.
await
może to zrobić. Gdy twoja metoda async
trafi w await
, metoda zostanie zablokowana, ale wątek powróci do puli wątków. Gdy metoda jest gotowa do kontynuowania, każdy wątek jest wyrwany z puli wątków i używany do wznowienia metoda.
Jedyna różnica ConfigureAwait
sprawia, że w ASP.NET czy wątek wchodzi w kontekst żądania podczas wznawiania metody.
Mam więcej informacji w moim MSDN artykuł na SynchronizationContext
i mój async
intro blog post .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 23:23:58
Krótka odpowiedź na twoje pytanie: nie. Nie powinieneś tak wywoływać ConfigureAwait(false)
na poziomie aplikacji.
TL; DR wersja długiej odpowiedzi: jeśli piszesz bibliotekę, w której nie znasz swojego konsumenta i nie potrzebujesz kontekstu synchronizacji (którego nie powinieneś w bibliotece, jak sądzę), powinieneś zawsze używać ConfigureAwait(false)
. W przeciwnym razie użytkownicy biblioteki mogą napotkać impasy, zużywając metody asynchroniczne w sposób blokujący. To zależy od sytuacji.
Tutaj jest nieco bardziej szczegółowe wyjaśnienie znaczenia metody ConfigureAwait
(cytat z mojego posta na blogu):
Gdy oczekujesz na metodę ze słowem kluczowym wait, kompilator generuje kod w Twoim imieniu. Jednym z celów tego akcja polega na obsłudze synchronizacji z wątkiem UI (lub głównym). Klucz składową tej funkcji jest
SynchronizationContext.Current
, która pobiera kontekst synchronizacji dla bieżącego wątku.SynchronizationContext.Current
jest wypełniana w zależności od środowisko, w którym się znajdujesz.GetAwaiter
metoda zadania wyszukuje dlaSynchronizationContext.Current
. Jeśli aktualny kontekst synchronizacji jest nie null, kontynuacja, która zostanie przekazana do tego wysłane z powrotem do kontekstu synchronizacji.Podczas używania metody, która używa nowego języka asynchronicznego funkcje, w sposób blokujący, skończysz z impasem, jeśli masz dostępną Synchronizacjękontekst. Podczas spożywania takie metody w sposób blokujący (czekanie na zadanie z Wait metoda lub pobierając wynik bezpośrednio z właściwości Result Zadanie), zablokujesz główny wątek w tym samym czasie. Kiedy ostatecznie zadanie kończy się wewnątrz tej metody w threadpool, to będzie wywoływać kontynuację, aby dodać powrót do głównego wątku ponieważ
SynchronizationContext.Current
jest dostępna i przechwycona. Ale jest tu problem: wątek UI jest zablokowany i masz impas!
Również, oto dwa świetne Artykuły dla Ciebie, które są dokładnie dla Twojego pytania:
- W 2008 roku, w ramach projektu "the C# 5.0", w ramach projektu "the C # 5.0", w ramach projektu "the C # 5.0", w ramach projektu "the C # 5.0", w ramach projektu "the C # 5.0", w ramach projektu " the C # 5.0.]}
- asynchroniczne Biblioteki klientów. NET dla Twojego HTTP API i świadomość złych efektów asynchronicznych / oczekujących
Wreszcie, jest świetny Krótki film z Lucian Wischik dokładnie na ten temat: metody biblioteki asynchronicznej powinny rozważyć użycie zadania.ConfigureAwait (false) .
Hope this pomaga.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-13 09:12:37
Największą wadą, jaką znalazłem przy użyciu ConfigureAwait (false), jest to, że kultura wątków jest przywracana do domyślnej wartości systemowej. Jeśli skonfigurowałeś kulturę np...
<system.web>
<globalization culture="en-AU" uiCulture="en-AU" />
...
I jesteś hosting na serwerze, którego kultura jest ustawiona na en-US, to znajdziesz przed ConfigureAwait (false) nazywa się CultureInfo.CurrentCulture zwróci en-AU, a następnie otrzymasz en-US. tj.
// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}
Jeśli Twoja aplikacja robi coś, co wymaga specyficznego formatowania danych, wtedy musisz o tym pamiętać podczas używania ConfigureAwait (false).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 03:12:32
Mam kilka ogólnych przemyśleń na temat implementacji Task
:
- zadanie jest jednorazowe, a my nie powinniśmy używać
using
. -
ConfigureAwait
został wprowadzony w 4.5.Task
został wprowadzony w 4.0. - . Net wątki zawsze używane do przepływu kontekstu (patrz C# poprzez książkę CLR), ale w domyślnej implementacji
Task.ContinueWith
nie b / c okazało się, że przełącznik kontekstowy jest drogi i jest domyślnie wyłączony. - problemem jest biblioteka deweloper nie powinien dbać o to, czy jego klienci potrzebują przepływu kontekstu, czy nie, dlatego nie powinien decydować, czy przepływ kontekstu, czy nie.
- [Dodano później] fakt, że nie ma autorytatywnej odpowiedzi i właściwego odniesienia, a my walczymy o to oznacza, że ktoś nie wykonał swojej pracy dobrze.
Mam kilka postów na ten temat, ale moim zdaniem - oprócz ładnej odpowiedzi Tugberka - jest to, że powinieneś włączyć wszystkie API asynchroniczne i najlepiej przepłynąć kontekst . ponieważ wykonujesz asynchronizację, możesz po prostu użyć kontynuacji zamiast czekać, aby nie spowodować impasu, ponieważ nie ma czekania w bibliotece i zachowujesz przepływ, aby kontekst był zachowany (np. HttpContext).
Problem polega na tym, że biblioteka wystawia synchroniczne API, ale używa innego asynchronicznego API - dlatego musisz użyć Wait()
/Result
w Twoim kodzie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 16:25:01