Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Możesz uniknąć wąskich gardeł wydajności i zwiększyć ogólną szybkość reakcji aplikacji przy użyciu programowania asynchronicznego. Jednak tradycyjne techniki pisania aplikacji asynchronicznych mogą być skomplikowane, co utrudnia pisanie, debugowanie i konserwację.
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ę wykonaną przez dewelopera, a aplikacja zachowuje strukturę logiczną przypominającą kod synchroniczny. W rezultacie uzyskasz wszystkie zalety programowania asynchronicznego z ułamkiem nakładu pracy.
Ten temat zawiera omówienie czasu i sposobu używania programowania asynchronicznego oraz zawiera linki do tematów pomocy technicznej zawierających szczegóły i 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 zasobu internetowego czasami jest 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ć inną pracę, która nie zależy od zasobu internetowego, dopóki potencjalnie blokujące zadanie nie zostanie zakończone.
W poniższej tabeli przedstawiono typowe obszary, w których programowanie asynchroniczne poprawia czas odpowiedzi. Wymienione interfejsy API z platformy .NET i środowiska uruchomieniowego systemu Windows zawierają metody obsługujące programowanie asynchroniczne.
Obszar aplikacji | Typy platformy .NET z metodami asynchronizowymi | Typy środowiska uruchomieniowego systemu Windows z metodami asynchronizowymi |
---|---|---|
Dostęp do internetu | 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 |
Asynchronia okazuje się szczególnie cenna w przypadku aplikacji, które uzyskują dostęp do wątku interfejsu użytkownika, ponieważ wszystkie operacje związane z interfejsem użytkownika zwykle dzielą jeden wątek. Jeśli jakikolwiek proces zostanie zablokowany w aplikacji synchronicznej, wszystkie zostaną zablokowane. Twoja aplikacja przestaje odpowiadać i możesz dojść do wniosku, że przestała działać, podczas gdy w rzeczywistości tylko czeka.
W przypadku używania metod asynchronicznych aplikacja nadal odpowiada na interfejs użytkownika. Możesz na przykład zmienić rozmiar lub zminimalizować okno albo zamknąć aplikację, jeśli nie chcesz czekać na jej zakończenie.
Podejście asynchroniczne dodaje odpowiednik automatycznej transmisji do listy opcji, które można wybrać podczas projektowania operacji asynchronicznych. Oznacza to, że uzyskujesz wszystkie korzyści z tradycyjnego programowania asynchronicznego, ale z znacznie mniejszym nakładem pracy od dewelopera.
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 środowisku uruchomieniowym 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ę asynchroniową. Prawie wszystko w kodzie powinno wyglądać znajomo.
Kompletny przykład Windows Presentation Foundation (WPF) jest dostępny do pobrania z sekcji Programowanie asynchroniczne z użyciem async 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 nauczyć się kilku praktyk. Zacznij od metody podpisu. Zawiera async
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
zadanie, otrzymasz string
(contents
). Zanim zaczniesz czekać na zadanie, możesz wykonać pracę, która nie zależy od string
z GetStringAsync
.
Zwróć szczególną await
uwagę na operatora. Zawiesza się GetUrlContentLengthAsync
:
-
GetUrlContentLengthAsync
nie może kontynuować, dopókigetStringTask
nie zostanie ukończone. - W tym czasie kontrola powraca do obiektu wywołującego
GetUrlContentLengthAsync
. - Sterowanie zostaje wznowione w tym miejscu po zakończeniu
getStringTask
. -
await
operator następnie pobierastring
wynik zgetStringTask
.
Instrukcja return określa rezultat całkowity. 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");
Oto charakterystyki, które sprawiają, że poprzedni przykład jest metodą asynchroniczną:
Podpis metody zawiera
async
modyfikator.Nazwa metody asynchronicznej zgodnie z konwencją kończy się sufiksem "Async".
Zwracany typ jest jednym z następujących typów:
-
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 międzyczasie metoda jest zawieszona, a kontrolka powraca do elementu wywołującego metodę. W następnej sekcji tego tematu przedstawiono, co dzieje się w punkcie zawieszenia.
W metodach asynchronicznych używasz podanych słów kluczowych i typów, aby wskazać, co chcesz zrobić, a kompilator wykonuje resztę, w tym śledzenie tego, co musi się zdarzyć, gdy kontrolka powróci do punktu oczekiwania w zawieszonej metodzie. Niektóre rutynowe procesy, takie jak pętle i obsługa wyjątków, mogą być trudne do obsługi w tradycyjnym kodzie asynchronicznym. W metodzie asynchronicznej zapisujesz te elementy tak samo jak w rozwiązaniu synchronicznym, a problem zostanie 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
Najważniejszą rzeczą, jaką należy zrozumieć w programowaniu asynchronicznym, jest sposób, w jaki przepływ sterowania przechodzi z 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łująca metoda wywołuje i oczekuje na asynchroniczną metodę
GetUrlContentLengthAsync
.GetUrlContentLengthAsync
tworzy instancję HttpClient, a następnie wywołuje asynchroniczną metodę GetStringAsync w celu pobrania zawartości witryny internetowej w postaci ciągu tekstowego.Coś dzieje się w
GetStringAsync
, co wstrzymuje jej postęp. Być może musi czekać na pobranie witryny 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 został oczekiwany,GetUrlContentLengthAsync
może kontynuować inne prace, które nie zależą od końcowego wyniku zGetStringAsync
. Ta praca jest reprezentowana przez wywołanie metodyDoIndependentWork
synchronicznej .DoIndependentWork
jest metodą synchroniczną, która wykonuje swoją pracę i wraca do wywołującego.GetUrlContentLengthAsync
skończyła się praca, którą można zrobić bez wyniku zgetStringTask
.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.W związku z tym
GetUrlContentLengthAsync
używa operatora await, aby zawiesić postęp i przekazać kontrolę metodzie, która wywołałaGetUrlContentLengthAsync
.GetUrlContentLengthAsync
zwracaTask<int>
do wywołującego. Zadanie reprezentuje obietnicę utworzenia wyniku całkowitego, który jest długością pobranego ciągu.Uwaga
Jeśli
GetStringAsync
(a zatemgetStringTask
) ukończy się, zanimGetUrlContentLengthAsync
będzie na niego czekać, kontrola pozostanie wGetUrlContentLengthAsync
. 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 przetwarzanie jest kontynuowane. Wywołujący może wykonać inną pracę, która nie zależy od wyniku z
GetUrlContentLengthAsync
, zanim zacznie na niego czekać, lub może czekać od razu. Metoda wywołująca czeka naGetUrlContentLengthAsync
, aGetUrlContentLengthAsync
czeka naGetStringAsync
.GetStringAsync
kończy i generuje wynik tekstowy. 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 potwierdzić, że program obsługi zdarzeń pobiera i wyświetla wartość wyniku długości. Jeśli dopiero zaczynasz programować asynchroniczne, rozważ różnicę między zachowaniem synchronicznym i asynchronicznym. Metoda synchroniczna zwraca wartość po zakończeniu pracy (krok 5), ale metoda asynchroniczna zwraca wartość zadania, gdy praca jest zawieszona (kroki 3 i 6). Gdy metoda asynchronizna ostatecznie zakończy swoją pracę, zadanie zostanie oznaczone jako ukończone, a wynik , jeśli istnieje, jest przechowywany w zadaniu.
Metody asynchroniczne interfejsu API
Być może zastanawiasz się, gdzie znaleźć metody takie jak GetStringAsync
, które wspierają programowanie asynchroniczne. Program .NET Framework 4.5 lub nowsza wersja oraz platforma .NET Core zawierają wiele elementów członkowskich współpracujących z async
i await
. Można je rozpoznać po sufiksie „Async”, który jest dołączany do nazwy elementu członkowskiego, oraz po ich typie zwracanym, którym jest Task lub Task<TResult>. Na przykład System.IO.Stream
klasa zawiera metody, takie jak CopyToAsync, ReadAsynci WriteAsync obok metod CopyTosynchronicznych , Readi Write.
Środowisko uruchomieniowe systemu Windows zawiera również wiele metod, których można używać z async
i await
w aplikacjach systemu Windows. 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 przewodnik: wywoływanie asynchronicznych interfejsów API w języku C# lub Visual Basic jeśli używasz wcześniejszych wersji środowiska uruchomieniowego systemu Windows.
Wątki
Metody asynchroniczne mają być operacjami nieblokacyjnymi. Wyrażenie await
w metodzie asynchronicznej nie blokuje bieżącego wątku, gdy oczekiwane zadanie jest uruchomione. Zamiast tego wyrażenie zarejestruje resztę metody jako kontynuację i zwraca kontrolkę do obiektu wywołującego metody asynchronicznej.
Słowa kluczowe async
i await
nie powodują tworzenia dodatkowych wątków. Metody asynchroniczne nie wymagają wielowątkowości, ponieważ nie działają na własnym wątku. Metoda jest uruchamiana w bieżącym kontekście synchronizacji i używa czasu w wątku tylko wtedy, gdy metoda jest aktywna. Możesz użyć Task.Run do przeniesienia prac CPU-zależnych do wątku w tle, ale wątek w tle nie pomaga w procesie, który po prostu czeka na dostępność wyników.
Podejście oparte na async do programowania asynchronicznego jest bardziej preferowane niż istniejące podejścia 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 z metodą Task.Run, programowanie asynchroniczne przewyższa BackgroundWorker w zadaniach obciążających CPU, ponieważ oddziela ono szczegóły koordynacji uruchamiania kodu od pracy, którą Task.Run
przenosi do puli wątków.
async oraz 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 asynchroniczna może używać await do wyznaczania punktów zawieszenia. Operator
await
informuje kompilator, że metoda asynchroniczna nie może być kontynuowana po tym punkcie, dopóki oczekiwany proces asynchroniczny nie zostanie ukończony. W międzyczasie kontrola powraca do obiektu wywołującego metodę async.Zawieszenie metody asynchronicznej w wyrażeniu
await
nie stanowi wyjścia z metody, a blokifinally
nie są wykonywane.Metoda oznaczona jako asynchroniczna może być oczekiwana przez metody, które ją wywołują.
Metoda asynchroniczna zazwyczaj zawiera jedno lub więcej wystąpień operatora await
, ale brak wyrażeń await
nie prowadzi do 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żenie dotyczące takich metod.
async
i await
są słowami kluczowymi kontekstowymi. Aby uzyskać więcej informacji i przykładów, zobacz następujące tematy:
Typy zwracane i parametry
Metoda asynchroniczna zwykle zwraca wartość Task lub Task<TResult>. Wewnątrz metody asynchronicznej operator await
jest stosowany do zadania, które jest zwracane z wywołania innej metody asynchronicznej.
Określasz Task<TResult> jako typ zwracany, jeśli metoda zawiera instrukcję return
, która określa 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ą element typu Task<TResult> 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 zwrócone zadanie reprezentuje bieżącą pracę. Zadanie zawiera informacje o stanie procesu asynchronicznego i ostatecznie obejmuje końcowy wynik procesu lub wyjątek zgłaszany przez 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 obsługi zdarzeń asynchronicznych często służą jako punkt wyjścia dla programów asynchronicznych.
Metoda asynchroniczna, która ma void
typ zwracany, nie może być oczekiwana, a wywołujący metodę zwracającą typ void nie może złapać żadnych wyjątków wyrzucanych przez tę metodę.
Metoda asynchroniczna nie może zadeklarować parametrów in, ref lub out, ale może wywoływać metody, które mają takie parametry. Podobnie, metoda asynchroniczna nie może zwrócić wartości przez referencję, 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 Windows Runtime mają jeden z następujących typów zwrotek, 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
, , Task<T>
ValueTask
, 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ę, w której 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 |
---|---|
Jak równolegle wykonywać wiele żądań internetowych przy użyciu async i await (C#) | Demonstruje sposób uruchamiania kilku zadań w tym samym czasie. |
Asynchroniczne typy zwracane (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. | Pokazuje, jak dodać następujące funkcje do rozwiązania asynchronicznego: - Anulowanie listy zadań (C#) - Anulowanie zadań po upływie czasu (C#) - Przetwarzanie zadań asynchronicznych w miarę ich ukończenia (C#) |
Używanie asynchronicznego dostępu do plików (C#) | Wyświetla listę i demonstruje zalety użycia async i await do 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 na Channel 9 | Zawiera linki do różnych filmów wideo dotyczących programowania asynchronicznego. |