Programowanie asynchroniczne z funkcją Async i Await (Visual Basic)
Artykuł
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
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.
' 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".AsyncFunction AccessTheWebAsync() As Task(OfInteger)
Using client AsNew 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(OfString) =
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 AsString = Await getStringTask
' The Return statement specifies an Integer result.' A method which awaits AccessTheWebAsync receives the Length value.Return urlContents.Length
EndUsingEndFunction
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ę.
VB
Dim urlContents AsString = 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.
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ą.
AccessTheWebAsynctworzy 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 i AccessTheWebAsync przypisuje zadanie do zmiennej getStringTask . Zadanie reprezentuje bieżący proces wywołania metody GetStringAsync, 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 z GetStringAsyncprogramu . Ta praca jest reprezentowana przez wywołanie metody DoIndependentWorksynchronicznej .
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 z getStringTask. 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 nazwie AccessTheWebAsync. AccessTheWebAsync zwraca obiekt do Task(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 dlatego getStringTask) zostanie ukończony przed AccessTheWebAsync jego oczekiwaniem, kontrolka pozostanie w AccessTheWebAsync. Koszt wstrzymania, a następnie powrotu do AccessTheWebAsync 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 na AccessTheWebAsyncelement i AccessTheWebAsync czeka na GetStringAsync.
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 metody getStringTask. Operator await pobiera wynik z getStringTaskelementu . Instrukcja przypisania przypisuje pobrany wynik do urlContents.
Gdy AccessTheWebAsync wynik ciągu, metoda może obliczyć długość ciągu. Następnie praca jest AccessTheWebAsync 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.
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.
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 , a Finally 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:
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
VB
' Signature specifies Task(Of Integer)AsyncFunction TaskOfTResult_MethodAsync() As Task(OfInteger)
Dim hours AsInteger' . . .' Return statement specifies an integer result.Return hours
EndFunction' Calls to TaskOfTResult_MethodAsyncDim returnedTaskTResult As Task(OfInteger) = TaskOfTResult_MethodAsync()
Dim intResult AsInteger = Await returnedTaskTResult
' or, in a single statementDim intResult AsInteger = Await TaskOfTResult_MethodAsync()
' Signature specifies TaskAsyncFunction Task_MethodAsync() As Task
' . . .' The method has no return statement.EndFunction' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statementAwait 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.
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:
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.
Przedstawia, w jaki sposób konwertować synchroniczne rozwiązanie WPF do asynchronicznego rozwiązania WPF. Ta aplikacja pobiera szereg witryn sieci Web.
Pokazuje, jak połączyć typy zadań w .NET Framework i IAsyncOperations w środowisko wykonawcze systemu Windows, aby można było użyć metody WhenAny środowisko wykonawcze systemu Windows.
Anulowanie asynchroniczne: łączenie platformy .NET Framework ze środowiskiem wykonawczym systemu Windows
Pokazuje, jak połączyć typy zadań w .NET Framework i IAsyncOperations w środowisko wykonawcze systemu Windows, aby można było użyć metody CancellationTokenSource środowisko wykonawcze systemu Windows.
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.PrivateAsyncSub StartButton_Click(sender AsObject, e As RoutedEventArgs)
' Call and await immediately.' StartButton_Click suspends until AccessTheWebAsync is done.Dim contentLength AsInteger = Await AccessTheWebAsync()
ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"EndSub' 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".AsyncFunction AccessTheWebAsync() As Task(OfInteger)
Using client AsNew 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(OfString) =
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 AsString = Await getStringTask
' The Return statement specifies an Integer result.' A method which awaits AccessTheWebAsync receives the Length value.Return urlContents.Length
EndUsingEndFunctionSub DoIndependentWork()
ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"EndSubEndClass
Dołącz do serii meetup, aby tworzyć skalowalne rozwiązania sztucznej inteligencji oparte na rzeczywistych przypadkach użycia z innymi deweloperami i ekspertami.