Model programowania asynchronicznego zadań (APM)
Możesz uniknąć problemów z wydajnością i poprawić ogólny czas odpowiedzi aplikacji, stosując programowanie asynchroniczne. Jednak tradycyjne techniki pisania aplikacji asynchronicznych mogą być skomplikowane, przez co trudne do pisania, debugowania i konserwacji.
Język C# obsługuje uproszczone podejście, programowanie asynchroniczne, które korzysta z asynchronicznej obsługi w środowisku uruchomieniowym platformy .NET. Kompilator wykonuje trudną pracę, którą kiedyś wykonywał programista, a aplikacja zachowuje strukturę logiczną przypominającą kod synchroniczny. W efekcie masz wszystkie korzyści wynikające z programowania asynchronicznego przy ułamkowym nakładzie pracy.
Ten temat zawiera omówienie, kiedy i jak stosować programowanie async oraz zawiera łącza do tematów zawierających szczegółowe informacje oraz przykłady.
Asynchronizacja poprawia czas odpowiedzi
Asynchronia jest niezbędna w przypadku działań, które potencjalnie blokują, takie jak dostęp do internetu. Dostęp do zasobów sieci Web bywa powolny lub opóźniony. Jeśli takie działanie zostanie zablokowane w procesie synchronicznym, cała aplikacja musi czekać. W procesie asynchronicznym aplikacja może kontynuować wykonywanie innych zadań, które nie są zależne od zasobów sieci Web, do momentu zakończeniem zadania mogącego powodować blokowanie.
W poniższej tabeli przedstawiono typowe obszary, w których programowanie asynchroniczne poprawia czas odpowiedzi. Wymienione interfejsy API z platformy .NET i środowisko wykonawcze systemu Windows zawierają metody obsługujące programowanie asynchroniczne.
Obszar aplikacji | Typy platformy .NET z metodami asynchronizowymi | środowisko wykonawcze systemu Windows typów za pomocą metod asynchronicznych |
---|---|---|
Web access | HttpClient | Windows.Web.Http.HttpClient SyndicationClient |
Praca z plikami | JsonSerializer StreamReader StreamWriter XmlReader XmlWriter |
StorageFile |
Praca z obrazami | MediaCapture BitmapEncoder BitmapDecoder |
|
Programowanie WCF | Operacje synchroniczne i asynchroniczne |
Asynchroniczność okazuje się szczególnie cenna w przypadku aplikacji, które mają dostęp do wątku interfejsu użytkownika, ponieważ wszystkie działania związane z interfejsem użytkownika korzystają zazwyczaj z jednego wątku. Jeśli w aplikacji synchronicznej jest zablokowany jakikolwiek proces, zablokowane są wszystkie procesy. Aplikacja przestanie odpowiadać i możesz dojść do wniosku, że uległa awarii, a tymczasem może po prostu być w stanie oczekiwania.
Kiedy używasz metod asynchronicznych, aplikacja dalej odpowiada interfejsowi użytkownika. Możesz przykładowo zmienić rozmiar lub zminimalizować aplikację lub możesz ją zamknąć, jeżeli nie chcesz czekać aż zakończy zadanie.
Podejście async oferuje również odpowiednik automatycznego przejścia do listy opcji do wybrania przy projektowaniu operacji asynchronicznych. Oznacza to, że można korzystać z wszystkich zalet tradycyjnego programowania asynchronicznego, ale przy znacznie mniejszym nakładzie pracy programisty.
Metody asynchroniczne są łatwe do zapisu
Słowa kluczowe asynchroniczne i await w języku C# są sercem programowania asynchronicznego. Używając tych dwóch słów kluczowych, możesz użyć zasobów w programie .NET Framework, .NET Core lub środowisko wykonawcze systemu Windows, aby utworzyć metodę asynchroniczną niemal tak łatwo, jak tworzysz metodę synchroniczną. Metody asynchroniczne definiowane za pomocą słowa kluczowego async
są określane jako metody asynchroniczne.
W poniższym przykładzie przedstawiono metodę async. Prawie wszystko w kodzie powinno wyglądać znajomo.
Kompletny przykład programu Windows Presentation Foundation (WPF) jest dostępny do pobrania z programowania asynchronicznego z funkcją asynchroniczną i await w języku C#.
public async Task<int> GetUrlContentLengthAsync()
{
using var client = new HttpClient();
Task<string> getStringTask =
client.GetStringAsync("https://learn.microsoft.com/dotnet");
DoIndependentWork();
string contents = await getStringTask;
return contents.Length;
}
void DoIndependentWork()
{
Console.WriteLine("Working...");
}
Z poprzedniego przykładu możesz zapoznać się z kilkoma rozwiązaniami. Zacznij od podpisu metody. async
Zawiera modyfikator. Zwracany typ to Task<int>
(zobacz sekcję "Zwracane typy", aby uzyskać więcej opcji). Nazwa metody kończy się na .Async
W treści metody GetStringAsync
zwraca wartość Task<string>
. Oznacza to, że gdy await
otrzymasz zadanie string
(contents
). Przed oczekiwaniem na zadanie możesz wykonać pracę, która nie polega na string
obiekcie z .GetStringAsync
Zwróć szczególną await
uwagę na operatora. GetUrlContentLengthAsync
Zawiesza się :
GetUrlContentLengthAsync
Nie można kontynuować, dopókigetStringTask
nie zostanie ukończona.- W międzyczasie kontrolka powraca do obiektu wywołującego .
GetUrlContentLengthAsync
- Kontrolka zostanie wznowiona w tym miejscu po
getStringTask
zakończeniu. - Następnie
await
operator pobierastring
wynik z .getStringTask
Instrukcja return określa wynik liczby całkowitej. Wszystkie metody oczekujące GetUrlContentLengthAsync
na pobranie wartości długości.
Jeśli GetUrlContentLengthAsync
nie ma żadnej pracy, którą może wykonać między wywołaniem GetStringAsync
i oczekiwaniem na jego ukończenie, możesz uprościć kod, wywołując i oczekując na następującą pojedynczą instrukcję.
string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");
Poniżej przedstawiono podsumowanie tego, co sprawia, że w poprzednim przykładzie metoda asynchronizuj:
Podpis metody zawiera
async
modyfikator.Nazwa metody async kończy się zwyczajowo sufiksem „Async”.
Zwracany typ może być jednym z następujących:
- Task<TResult> jeśli metoda ma instrukcję return, w której operand ma typ
TResult
. - Task jeśli metoda nie ma instrukcji return lub ma instrukcję return bez operandu.
void
jeśli piszesz asynchroniczny program obsługi zdarzeń.- Każdy inny typ, który ma metodę
GetAwaiter
.
Aby uzyskać więcej informacji, zobacz sekcję Zwracane typy i parametry .
- Task<TResult> jeśli metoda ma instrukcję return, w której operand ma typ
Metoda zwykle zawiera co najmniej jedno
await
wyrażenie, które oznacza punkt, w którym metoda nie może kontynuować, dopóki nie zostanie ukończona oczekiwana operacja asynchroniczna. W tym czasie metoda jest zawieszona, a sterowanie powraca do obiektu wywołującego metodę. Następna sekcji tego tematu przedstawia, co się dzieje w punkcie zawieszenia.
W metodzie asynchronicznej używasz podanych słów kluczowych i typów w celu wskazania, co chcesz zrobić, a kompilator zajmie się resztą, w tym śledzeniem tego, co musi się zdarzyć, gdy sterowanie powraca do punktu oczekiwania w metodzie zawieszonej. Niektóre procesy, takie jak pętle i obsługa wyjątków, mogą być trudne do obsłużenia w tradycyjnym kodzie asynchronicznym. W metodzie asynchronicznej wpisujesz te elementy podobnie jak w rozwiązaniu synchronicznym i problem rozwiązany.
Aby uzyskać więcej informacji na temat asynchronii w poprzednich wersjach programu .NET Framework, zobacz TPL i tradycyjne programowanie asynchroniczne programu .NET Framework.
Co się dzieje w metodzie asynchronicznej
Ważne jest, aby rozumieć programowanie asynchroniczne jako przepływ sterowania od metody do metody. Na poniższym diagramie przedstawiono proces:
Liczby na diagramie odpowiadają poniższym krokom zainicjowanym, gdy metoda wywołująca wywołuje metodę asynchroniową.
Wywołanie metody wywołującej i oczekiwanie na metodę asynchroniową
GetUrlContentLengthAsync
.GetUrlContentLengthAsync
Tworzy wystąpienie i wywołuje GetStringAsync metodę HttpClient asynchroniczną w celu pobrania zawartości witryny internetowej jako ciągu.Coś się dzieje w
GetStringAsync
tym, że zawiesza swój postęp. Być może metoda musi czekać na pobranie strony internetowej lub inne działanie blokujące. Aby uniknąć blokowania zasobów,GetStringAsync
daje kontrolę nad obiektem wywołującym,GetUrlContentLengthAsync
.GetStringAsync
Zwraca wartość Task<TResult>, gdzieTResult
jest ciągiem iGetUrlContentLengthAsync
przypisuje zadanie do zmiennejgetStringTask
. Zadanie reprezentuje ciągły proces wywołania metodyGetStringAsync
, z zobowiązaniem do utworzenia rzeczywistej wartości ciągu po zakończeniu pracy.Ponieważ
getStringTask
jeszcze nie oczekiwano, może kontynuować inne prace,GetUrlContentLengthAsync
które nie zależą od końcowego wyniku z .GetStringAsync
Ta praca jest reprezentowana przez wywołanie metodyDoIndependentWork
synchronicznej .DoIndependentWork
jest metodą synchroniczną, która wykonuje swoją pracę i wraca do elementu wywołującego.GetUrlContentLengthAsync
zabrakło pracy, którą można zrobić bez wyniku z .getStringTask
GetUrlContentLengthAsync
Następnie chce obliczyć i zwrócić długość pobranego ciągu, ale metoda nie może obliczyć tej wartości, dopóki metoda nie ma ciągu.GetUrlContentLengthAsync
W związku z tym używa operatora await, aby zawiesić jego postęp i kontrolować metodę o nazwieGetUrlContentLengthAsync
.GetUrlContentLengthAsync
metoda zwraca elementTask<int>
do elementu wywołującego. Zadanie przedstawia obietnicę utworzenia w wyniku liczby całkowitej, która jest długością pobranego ciągu.Uwaga
Jeśli
GetStringAsync
(i w związku z tymgetStringTask
) zostanie ukończony przed jego oczekiwaniemGetUrlContentLengthAsync
, kontrolka pozostanie w .GetUrlContentLengthAsync
Koszt wstrzymania, a następnie powrotuGetUrlContentLengthAsync
do zostanie zmarnowany, jeśli wywołany procesgetStringTask
asynchroniczny został już zakończony iGetUrlContentLengthAsync
nie musi czekać na końcowy wynik.Wewnątrz metody wywołującej wzorzec przetwarzania jest kontynuowany. Obiekt wywołujący może wykonać inną pracę, która nie zależy od wyniku przed
GetUrlContentLengthAsync
oczekiwaniem na ten wynik, lub obiekt wywołujący może oczekiwać natychmiast. Metoda wywołująca czeka naGetUrlContentLengthAsync
element iGetUrlContentLengthAsync
czeka naGetStringAsync
.GetStringAsync
kończy i generuje wynik ciągu. Wynik ciągu nie jest zwracany przez wywołanie metodyGetStringAsync
w oczekiwany sposób. (Pamiętaj, że metoda zwróciła już zadanie w kroku 3). Zamiast tego wynik ciągu jest przechowywany w zadaniu reprezentującym ukończenie metodygetStringTask
. Operator await pobiera wynik z elementugetStringTask
. Instrukcja przypisania przypisuje pobrany wynik docontents
.Gdy
GetUrlContentLengthAsync
wynik ciągu, metoda może obliczyć długość ciągu. Następnie praca programuGetUrlContentLengthAsync
jest również zakończona, a program obsługi zdarzeń oczekiwania może wznawiać. W pełnym przykładzie na końcu tematu można zobaczyć, że program obsługi zdarzeń pobiera i drukuje wynikową wartość długości. Jeśli dopiero zaczynasz przygodę z programowaniem asynchronicznym, zastanów się przez chwilę, jaka jest różnica między zachowaniem synchronicznym i asynchronicznym. Metoda synchroniczna kończy działanie, gdy praca jest zakończona (krok 5), natomiast metoda asynchroniczna zwraca wartość zadania, gdy jej praca jest zawieszona (kroki 3 i 6). Gdy metoda async ukończy pracę, zadanie jest oznaczane jako ukończone, a wynik, o ile istnieje, jest zapisywany w zadaniu.
Metody asynchroniczne interfejsu API
Być może zastanawiasz się, gdzie znaleźć metody, takie jak GetStringAsync
obsługa programowania asynchronicznego. Program .NET Framework 4.5 lub nowszy i platforma .NET Core zawierają wiele elementów członkowskich, z którymi współpracuje program async
i await
. Można je rozpoznać za pomocą sufiksu "Async", który jest dołączany do nazwy elementu członkowskiego, oraz przez ich zwracany typ Task lub Task<TResult>. Na przykład System.IO.Stream
klasa zawiera metody, takie jak CopyToAsync, ReadAsynci WriteAsync obok metod CopyTosynchronicznych , Readi Write.
Środowisko wykonawcze systemu Windows zawiera również wiele metod, których można używać z aplikacjami async
systemu Windows i await
. Aby uzyskać więcej informacji, zobacz Tworzenie wątków i programowanie asynchroniczne dla platformy UWP oraz Programowanie asynchroniczne (aplikacje ze Sklepu Windows) i Szybki start: wywoływanie asynchronicznych interfejsów API w języku C# lub Visual Basic, jeśli używasz wcześniejszych wersji środowisko wykonawcze systemu Windows.
Wątki
Metody async mają być operacjami niepowodującymi blokowania. Wyrażenie await
w metodzie asynchronicznej nie blokuje bieżącego wątku, gdy oczekiwane zadanie jest uruchomione. Zamiast tego, wyrażenie rejestruje pozostałą część metody jako kontynuację i przekazuje sterowanie do obiektu wywołującego metody async.
Słowa async
kluczowe i await
nie powodują tworzenia dodatkowych wątków. Metody komunikacji async nie wymagają wielowątkowości, ponieważ nie działają we własnym wątku. Metoda działa w bieżącym kontekście synchronizacji i używa czasu wątku, tylko wtedy, gdy jest aktywna. Możesz użyć Task.Run polecenia , aby przenieść pracę związaną z procesorem CPU do wątku w tle, ale wątek w tle nie pomaga w procesie, który po prostu czeka na udostępnienie wyników.
Podejście async do programowania asynchronicznego jest preferowane prawie w każdym przypadku. W szczególności takie podejście jest lepsze niż BackgroundWorker klasa operacji związanych z operacjami we/wy, ponieważ kod jest prostszy i nie musisz chronić się przed warunkami wyścigu. W połączeniu Task.Run z metodą programowanie asynchroniczne jest lepsze niż BackgroundWorker w przypadku operacji związanych z procesorem CPU, ponieważ programowanie asynchroniczne oddziela szczegóły koordynacji uruchamiania kodu od pracy, która Task.Run
przenosi się do puli wątków.
async i await
Jeśli określisz, że metoda jest metodą asynchronizującą przy użyciu modyfikatora asynchronicznego, włącz następujące dwie możliwości.
Oznaczona metoda asynchronizna może służyć do wyznaczania punktów zawieszenia. Operator
await
informuje kompilator, że metoda asynchroniczna nie może kontynuować przeszłości tego punktu, dopóki nie zostanie ukończony oczekiwany proces asynchroniczny. W międzyczasie sterowanie powraca do obiektu wywołującego metodę async.Zawieszenie metody asynchronicznej w wyrażeniu
await
nie stanowi wyjścia z metody, afinally
bloki nie są uruchamiane.Metoda oznaczona jako async sama może być oczekiwana przez metody, które ją wywołują.
Metoda asynchronicznie zwykle zawiera jedno lub więcej wystąpień await
operatora, ale brak await
wyrażeń nie powoduje błędu kompilatora. Jeśli metoda asynchroniczna nie używa await
operatora do oznaczania punktu zawieszenia, metoda jest wykonywana jako metoda synchroniczna, pomimo async
modyfikatora. Kompilator generuje ostrzeżenia dla takich metod.
async
i await
są słowami kluczowymi kontekstowymi. Aby uzyskać więcej informacji i przykładów, zobacz następujący temat:
Zwracane typy i parametry
Metoda asynchronicznie zwykle zwraca wartość Task lub Task<TResult>. Wewnątrz metody await
asynchronicznej operator jest stosowany do zadania zwróconego z wywołania do innej metody asynchronicznej.
Typ zwracany określa Task<TResult> się, jeśli metoda zawiera instrukcję return
określającą operand typu TResult
.
Jest używany Task jako typ zwracany, jeśli metoda nie ma instrukcji return lub ma instrukcję return, która nie zwraca operandu.
Można również określić dowolny inny typ zwracany, pod warunkiem, że typ zawiera metodę GetAwaiter
. ValueTask<TResult> jest przykładem takiego typu. Jest on dostępny w pakiecie NuGet System.Threading.Tasks.Extension .
W poniższym przykładzie pokazano, jak zadeklarować i wywołać metodę zwracającą Task<TResult> obiekt lub Task:
async Task<int> GetTaskOfTResultAsync()
{
int hours = 0;
await Task.Delay(0);
return hours;
}
Task<int> returnedTaskTResult = GetTaskOfTResultAsync();
int intResult = await returnedTaskTResult;
// Single line
// int intResult = await GetTaskOfTResultAsync();
async Task GetTaskAsync()
{
await Task.Delay(0);
// No return statement needed
}
Task returnedTask = GetTaskAsync();
await returnedTask;
// Single line
await GetTaskAsync();
Każde zwracane zadanie reprezentuje zadanie pracy w toku. Zadanie zawiera informacje o stanie procesu asynchronicznego i, ostatecznie, albo ostatecznego wyniku procesu lub wyjątku, który zgłasza proces, jeśli się nie powiedzie.
Metoda asynchronizna może również mieć typ zwracany void
. Ten typ zwracany jest używany głównie do definiowania procedur obsługi zdarzeń, w których wymagany jest zwracany void
typ. Programy async obsługi zdarzeń często służą jako punkt wejścia dla programów async.
Nie można oczekiwać metody asynchronicznej, która ma void
typ zwracany, a obiekt wywołujący metody zwracanej przez pustkę nie może przechwytywać żadnych wyjątków zgłaszanych przez metodę.
Metoda asynchronizuj nie może zadeklarować parametrów w parametrach ref lub out , ale metoda może wywoływać metody, które mają takie parametry. Podobnie metoda asynchronicznie nie może zwrócić wartości według odwołania, chociaż może wywołać metody z wartościami zwracanymi ref.
Aby uzyskać więcej informacji i przykładów, zobacz Async return types (C#)( Typy zwracane asynchroniczne (C#).
Asynchroniczne interfejsy API w programowaniu środowisko wykonawcze systemu Windows mają jeden z następujących typów zwracanych, które są podobne do zadań:
- IAsyncOperation<TResult>, który odpowiada Task<TResult>
- IAsyncAction, który odpowiada Task
- IAsyncActionWithProgress<TProgress>
- IAsyncOperationWithProgress<TResult,TProgress>
Konwencja nazewnictwa
Zgodnie z konwencją metody, które zwracają często oczekiwane typy (na przykład Task
, , ValueTask
Task<T>
, ValueTask<T>
) powinny mieć nazwy kończące się ciągiem "Async". Metody, które rozpoczynają operację asynchroniczną, ale nie zwracają oczekiwanego typu, nie powinny mieć nazw kończących się ciągiem "Async", ale mogą zaczynać się od "Begin", "Start" lub innego zlecenia sugerującego, że ta metoda nie zwraca ani nie zgłasza wyniku operacji.
Można zignorować konwencję, gdy zdarzenie, klasa bazowa lub kontrakt interfejsu sugeruje inną nazwę. Na przykład nie należy zmieniać nazw typowych programów obsługi zdarzeń, takich jak OnButtonClick
.
Powiązane artykuły (Visual Studio)
Nazwa | opis |
---|---|
Równoległe tworzenie wielu żądań internetowych przy użyciu asynchronicznego i await (C#) | Ilustruje, jak uruchomić kilka zadań w tym samym czasie. |
Typy zwracane asynchroniczne (C#) | Ilustruje typy, które mogą zwracać metody asynchroniczne, i wyjaśnia, kiedy każdy typ jest odpowiedni. |
Anulowanie zadań przy użyciu tokenu anulowania jako mechanizmu sygnalizacyjnego. | Przedstawia, w jaki sposób dodać następujące funkcje do rozwiązania async: - Anulowanie listy zadań (C#) - Anulowanie zadań po upływie czasu (C#) - Przetwarzanie zadania asynchronicznego podczas ich wykonywania (C#) |
Używanie asynchronicznego dostępu do plików (C#) | Wyświetla listę korzyści wynikających ze stosowania słów kluczowych async i await przy uzyskiwaniu dostępu do plików. |
Wzorzec asynchroniczny oparty na zadaniach (TAP) | Opisuje wzorzec asynchroniczny, wzorzec jest oparty na typach Task i Task<TResult> . |
Asynchroniczne filmy wideo w witrynie Channel 9 | Oferuje łącza do różnych plików wideo dotyczących programowania asynchronicznego. |