Programowanie asynchroniczne z funkcją Async i Await (Visual Basic)
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.
Visual Studio 2012 r. wprowadzono uproszczone podejście, programowanie asynchroniczne, które wykorzystuje asynchroniczne wsparcie w .NET Framework 4.5 i nowszych, a także w środowisko wykonawcze systemu Windows. 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.
Asynchroniczne poprawia czas reakcji
Asynchroniczność jest istotna dla działań, które mogą powodować blokowanie, na przykład, gdy aplikacja uzyskuje dostęp do sieci Web. Dostęp do zasobów sieci Web bywa powolny lub opóźniony. Jeśli takie działanie jest zablokowane w procesie synchronicznym, cała aplikacji 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 .NET Framework 4.5 i środowisko wykonawcze systemu Windows zawierają metody obsługujące programowanie asynchroniczne.
Obszar aplikacji | Obsługa interfejsów API, która obejmuje metody asynchroniczne |
---|---|
Web access | HttpClient, SyndicationClient |
Praca z plikami | StorageFile, StreamWriter, StreamReader, XmlReader |
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ą łatwiejsze do zapisu
Słowa kluczowe Async i Await w Visual Basic są sercem programowania asynchronicznego. Używając tych dwóch słów kluczowych, możesz użyć zasobów w .NET Framework lub środowisko wykonawcze systemu Windows, aby utworzyć metodę asynchroniczną niemal tak łatwo, jak tworzysz metodę synchroniczną. Metody asynchroniczne definiowane przy użyciu metody Async
i Await
są określane jako metody asynchroniczne.
W poniższym przykładzie przedstawiono metodę async. Prawie wszystko w kodzie powinno wyglądać znajomo. Komentarze wywołują funkcje dodawane przez użytkownika w celu uzyskania asynchroniczności.
Pełny plik przykładowy Windows Presentation Foundation (WPF) można znaleźć na końcu tego tematu i pobrać przykład z przykładu Async Sample: Example from "Asynchronous Programming with Async and Await" (Asynchroniczne programowanie za pomocą asynchronicznego i await).
' Three things to note about writing an Async Function:
' - The function has an Async modifier.
' - Its return type is Task or Task(Of T). (See "Return Types" section.)
' - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
Using client As New HttpClient()
' Call and await separately.
' - AccessTheWebAsync can do other things while GetStringAsync is also running.
' - getStringTask stores the task we get from the call to GetStringAsync.
' - Task(Of String) means it is a task which returns a String when it is done.
Dim getStringTask As Task(Of String) =
client.GetStringAsync("https://docs.microsoft.com/dotnet")
' You can do other work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork()
' The Await operator suspends AccessTheWebAsync.
' - AccessTheWebAsync does not continue until getStringTask is complete.
' - Meanwhile, control returns to the caller of AccessTheWebAsync.
' - Control resumes here when getStringTask is complete.
' - The Await operator then retrieves the String result from getStringTask.
Dim urlContents As String = Await getStringTask
' The Return statement specifies an Integer result.
' A method which awaits AccessTheWebAsync receives the Length value.
Return urlContents.Length
End Using
End Function
Jeśli AccessTheWebAsync
nie ma żadnej pracy, którą może wykonać między wywołaniem GetStringAsync
i oczekiwaniem na ukończenie, możesz uprościć kod, wywołując i oczekując na następującą pojedynczą instrukcję.
Dim urlContents As String = Await client.GetStringAsync()
Poniższe charakterystyki zawierają podsumowanie tego, co sprawia, że poprzedni przykład jest metodą asynchronikową:
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(Of 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.
- Sub , jeśli piszesz asynchroniczny program obsługi zdarzeń.
Aby uzyskać więcej informacji, zobacz „Typy zwracane i parametry” w dalszej części tego tematu.
Metoda zazwyczaj zawiera co najmniej jedno wyrażenie „await”, które oznacza punkt, w którym metoda nie może kontynuować pracy do czasu ukończenia operacji asynchronicznej. 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 .NET Framework, zobacz TPL i Tradycyjne .NET Framework Asynchroniczne programowanie.
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ą następującym krokom:
Program obsługi zdarzeń wywołuje i oczekuje na metodę
AccessTheWebAsync
asynchronizową.AccessTheWebAsync
tworzy wystąpienie i wywołuje GetStringAsync metodę HttpClient asynchroniczną, aby pobrać zawartość witryny internetowej jako ciąg.Coś się dzieje w
GetStringAsync
tym zawiesza jego 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.AccessTheWebAsync
GetStringAsync
Zwraca zadanie (Z TResult), gdzie TResult jest ciągiem iAccessTheWebAsync
przypisuje zadanie do zmiennejgetStringTask
. Zadanie reprezentuje bieżący proces wywołania metodyGetStringAsync
, z zobowiązaniem do wygenerowania rzeczywistej wartości ciągu po zakończeniu pracy.Ponieważ
getStringTask
nie oczekiwano jeszcze,AccessTheWebAsync
może kontynuować inne prace, które nie zależą od końcowego wyniku zGetStringAsync
programu . Ta praca jest reprezentowana przez wywołanie metodyDoIndependentWork
synchronicznej .DoIndependentWork
to metoda synchroniczna, która wykonuje swoją pracę i powraca do elementu wywołującego.AccessTheWebAsync
zabrakło pracy, że może to zrobić bez wyniku zgetStringTask
.AccessTheWebAsync
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 będzie mieć ciągu.AccessTheWebAsync
W związku z tym używa operatora await, aby zawiesić jego postęp i kontrolować metodę o nazwieAccessTheWebAsync
.AccessTheWebAsync
zwraca obiekt doTask(Of Integer)
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 dlategogetStringTask
) zostanie ukończony przedAccessTheWebAsync
jego oczekiwaniem, kontrolka pozostanie wAccessTheWebAsync
. Koszt wstrzymania, a następnie powrotu doAccessTheWebAsync
zostanie zmarnowany, jeśli wywołany proces asynchroniczny (getStringTask
) () został już ukończony, a program AccessTheWebSync nie musi czekać na końcowy wynik.Przetwarzanie wzorca jest kontynuowane wewnątrz elementu wywołującego (programu obsługi zdarzeń w tym przykładzie). Obiekt wywołujący może wykonać inną pracę, która nie zależy od wyniku przed
AccessTheWebAsync
oczekiwaniem na ten wynik, lub obiekt wywołujący może oczekiwać natychmiast. Program obsługi zdarzeń czeka naAccessTheWebAsync
element iAccessTheWebAsync
czeka naGetStringAsync
.GetStringAsync
kończy i generuje wynik ciągu. Wynik ciągu nie jest zwracany przez wywołanie metody w sposób, który można oczekiwaćGetStringAsync
. (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 zgetStringTask
elementu . Instrukcja przypisania przypisuje pobrany wynik dourlContents
.Gdy
AccessTheWebAsync
wynik ciągu, metoda może obliczyć długość ciągu. Następnie praca jestAccessTheWebAsync
również ukończona, a oczekujące procedury obsługi zdarzeń mogą być wznowione. 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.
Aby uzyskać więcej informacji na temat przepływu sterowania, zobacz Control Flow in Async Programs (Visual Basic).
Metody asynchroniczne interfejsu API
Być może zastanawiasz się, gdzie znaleźć metody, takie jak GetStringAsync
ta obsługa programowania asynchronicznego. .NET Framework 4.5 lub nowszym zawiera wiele elementów członkowskich, które współpracują z elementami Async
i Await
. Można rozpoznać te elementy członkowskie za pomocą sufiksu "Async", który jest dołączony do nazwy elementu członkowskiego i typu zwracanego Task lub Task(Of 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
i Await
w Windows. Aby uzyskać więcej informacji i przykładowych metod, zobacz Wywoływanie asynchronicznych interfejsów API w języku C# lub Visual Basic, programowanie asynchroniczne(aplikacje środowisko wykonawcze systemu Windows) i WhenAny: mostkowanie między .NET Framework a aplikacjami ś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ą utworzenia dodatkowych wątków. Metody asynchroniczne nie wymagają wielowątkowego, ponieważ metoda asynchroniczna nie jest uruchamiana 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 w przypadku operacji związanych z operacjami we/wy, ponieważ kod jest prostszy i nie musisz chronić się przed warunkami wyścigu. W połączeniu z programem programowanie asynchroniczne jest lepsze niż BackgroundWorker w przypadku operacji związanych z Task.Runprocesorem CPU, ponieważ programowanie asynchroniczne oddziela szczegóły koordynacji uruchamiania kodu od pracy przesyłanej Task.Run
do puli wątków.
Async i Await
Jeśli określisz, że metoda jest metodą asynchroniczna przy użyciu modyfikatora asynchronicznego , włączysz następujące dwie możliwości.
Oznaczona metoda asynchroniczna może użyć metody Await do wyznaczenia punktów zawieszenia. Operator await informuje kompilator, że metoda async nie może kontynuować działania do chwili zakończenia procesu asynchronicznego, na który oczekuje. 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 asynchroniczna 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
W .NET Framework programowania metoda asynchroniczna zwykle zwraca Task wartość lub task(Of TResult). Wewnątrz metody Await
asynchronicznej operator jest stosowany do zadania zwróconego z wywołania do innej metody asynchronicznej.
Zadanie (Z TResult) należy określić 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.
W poniższym przykładzie pokazano, jak zadeklarować i wywołać metodę zwracającą metodę Task(Of TResult) lub :Task
' Signature specifies Task(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)
Dim hours As Integer
' . . .
' Return statement specifies an integer result.
Return hours
End Function
' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()
' Signature specifies Task
Async Function Task_MethodAsync() As Task
' . . .
' The method has no return statement.
End Function
' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()
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 asynchroniczna może być Sub
również metodą . Ten zwracany typ jest używany głównie do definiowania procedur obsługi zdarzeń, w których wymagany jest typ zwracany. Programy async obsługi zdarzeń często służą jako punkt wejścia dla programów async.
Nie można oczekiwać metody asynchronicznej, której Sub
nie można oczekiwać, a obiekt wywołujący nie może przechwycić żadnych wyjątków zgłaszanych przez metodę.
Metoda asynchroniczna nie może zadeklarować parametrów ByRef , ale metoda może wywoływać metody, które mają takie parametry.
Aby uzyskać więcej informacji i przykłady, zobacz Async Return Types (Visual Basic). Aby uzyskać więcej informacji na temat przechwytywania wyjątków w metodach asynchronicznych, zobacz Try... Złapać... Finally, instrukcja.
Asynchroniczne interfejsy API w programowaniu środowisko wykonawcze systemu Windows mają jeden z następujących typów zwracanych, które są podobne do zadań podrzędnych:
- IAsyncOperation(Of TResult), który odpowiada zadaniu (of TResult)
- IAsyncAction, który odpowiada Task
- IAsyncActionWithProgress(Of TProgress)
- IAsyncOperationWithProgress(Of TResult, TProgress)
Aby uzyskać więcej informacji i przykład, zobacz Wywoływanie asynchronicznych interfejsów API w języku C# lub Visual Basic.
Konwencja nazewnictwa
Zgodnie z konwencją dołączasz ciąg "Async" do nazw metod, które mają Async
modyfikator.
Można zignorować konwencję, gdy zdarzenie, klasa bazowa lub kontrakt interfejsu sugeruje inną nazwę. Na przykład nie należy zmieniać nazw typowych procedur obsługi zdarzeń, takich jak Button1_Click
.
Powiązane tematy i przykłady (Visual Studio)
Kompletny przykład
Poniższy kod to plik MainWindow.xaml.vb z aplikacji Windows Presentation Foundation (WPF), którą omówiono w tym temacie. Przykład można pobrać z przykładu asynchronicznego: przykład z sekcji "Programowanie asynchroniczne za pomocą Async i Await".
Imports System.Net.Http
' Example that demonstrates Asynchronous Progamming with Async and Await.
' It uses HttpClient.GetStringAsync to download the contents of a website.
' Sample Output:
' Working . . . . . . .
'
' Length of the downloaded string: 39678.
Class MainWindow
' Mark the event handler with Async so you can use Await in it.
Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
' Call and await immediately.
' StartButton_Click suspends until AccessTheWebAsync is done.
Dim contentLength As Integer = Await AccessTheWebAsync()
ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"
End Sub
' Three things to note about writing an Async Function:
' - The function has an Async modifier.
' - Its return type is Task or Task(Of T). (See "Return Types" section.)
' - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
Using client As New HttpClient()
' Call and await separately.
' - AccessTheWebAsync can do other things while GetStringAsync is also running.
' - getStringTask stores the task we get from the call to GetStringAsync.
' - Task(Of String) means it is a task which returns a String when it is done.
Dim getStringTask As Task(Of String) =
client.GetStringAsync("https://docs.microsoft.com/dotnet")
' You can do other work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork()
' The Await operator suspends AccessTheWebAsync.
' - AccessTheWebAsync does not continue until getStringTask is complete.
' - Meanwhile, control returns to the caller of AccessTheWebAsync.
' - Control resumes here when getStringTask is complete.
' - The Await operator then retrieves the String result from getStringTask.
Dim urlContents As String = Await getStringTask
' The Return statement specifies an Integer result.
' A method which awaits AccessTheWebAsync receives the Length value.
Return urlContents.Length
End Using
End Function
Sub DoIndependentWork()
ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"
End Sub
End Class