Sdílet prostřednictvím


Řízení toku v asynchronních programech (C# a Visual Basic)

Můžete napsat a snadněji udržovat asynchronní programy pomocí klíčových slov Async a Await.Pokud nevíte, jak program pracuje, výsledky vás mohou pravděpodobně zaskočit.Toto téma sleduje tok řízení prostřednictvím jednoduchého asynchronního programu k ukázání, kdy se ovládací prvek přesune z jedné metody na jinou a jaké informace jsou pokaždé přeneseny.

[!POZNÁMKA]

Klíčová slova Async a Await byla zavedena v aplikaci Visual Studio 2012.

Obecně označujete metody, které obsahují asynchronní kód, pomocí modifikátoru Async (Visual Basic) nebo async (C#).V metodě, která je označena asynchronním modifikátorem, můžete použít operátor Await (Visual Basic) nebo await (C#) chcete-li určit, kde metoda počká na dokončení volaného asynchronního procesu.Další informace naleznete v tématu Asynchronní programování pomocí modifikátoru Async a operátoru Await (C# a Visual Basic).

Následující příklad používá asynchronní metody ke stahování obsahu zadaného webu jako řetězec a zobrazí délku řetězce.Tento příklad obsahuje následující dvě metody.

  • startButton_Click, které volá AccessTheWebAsync a zobrazí výsledek.

  • AccessTheWebAsync, který stáhne obsah webu jako řetězec a vrátí délku řetězce.AccessTheWebAsync používá asynchronní metodu HttpClient, GetStringAsync(String), pro stažení obsahu.

Číslované řádky zobrazení se zobrazí jako strategická místa v celém programu, která vám pomohou pochopit, jak program funguje a co se stane v každém bodě, který je označen.Zobrazené řádky jsou označeny "JEDNA" až "ŠEST." Popisky představují pořadí, ve kterém dosáhne program tyto řádky kódu.

Následující kód ukazuje přehled programu.

Class MainWindow

    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click

        ' ONE
        Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

        ' FOUR
        Dim contentLength As Integer = Await getLengthTask

        ' SIX
        ResultsTextBox.Text &=
            String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)

    End Sub


    Async Function AccessTheWebAsync() As Task(Of Integer)

        ' TWO
        Dim client As HttpClient = New HttpClient() 
        Dim getStringTask As Task(Of String) = 
            client.GetStringAsync("https://msdn.microsoft.com")

        ' THREE
        Dim urlContents As String = Await getStringTask

        ' FIVE
        Return urlContents.Length
    End Function

End Class
public partial class MainWindow : Window
{
    // . . .
    private async void startButton_Click(object sender, RoutedEventArgs e)
    {
        // ONE
        Task<int> getLengthTask = AccessTheWebAsync();

        // FOUR
        int contentLength = await getLengthTask;

        // SIX
        resultsTextBox.Text +=
            String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
    }


    async Task<int> AccessTheWebAsync()
    {
        // TWO
        HttpClient client = new HttpClient();
        Task<string> getStringTask =
            client.GetStringAsync("https://msdn.microsoft.com");

        // THREE                 
        string urlContents = await getStringTask;

        // FIVE
        return urlContents.Length;
    }
}

Každé z označených míst „jedna“ až „šest“ obsahuje informace o aktuálním stavu programu.Následující výstup je vytvořen.

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.

TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Length of the downloaded string: 33946.

Vytvoření programu

Kód, který používá toto téma, si můžete stáhnout z webu MSDN, nebo si ho můžete vytvořit sami.

[!POZNÁMKA]

Chcete-li spustit příklad, musíte mít nainstalovanou aplikaci Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012, Visual Studio Express 2013 for Windows nebo rozhraní .NET Framework 4.5 nebo 4.5.1 v počítači.

Stažení programu

Aplikaci pro toto téma si můžete stáhnout v tématu Asynchronní vzorek: Tok řízení v asynchronních programech.Následující postup otevře a spustí program.

  1. Rozbalte soubor, který jste stáhli, a poté spusťte Visual Studio.

  2. Na panelu nabídky vyberte možnosti Soubor, Otevřít, Projekt/řešení.

  3. Přejděte do složky obsahující dekomprimovaný ukázkový kód, otevřete soubor řešení (.sln) a poté pomocí klávesy F5 sestavte a spusťte projekt.

Vytvoření programu vlastními silami

Následující projekt Windows Presentation Foundation (WPF) obsahuje příklad kódu pro toto téma.

Chcete-li spustit projekt, proveďte následující kroky:

  1. Spusťte aplikaci Visual Studio.

  2. Na panelu nabídky vyberte možnosti Soubor, Nový, Projekt.

    Otevře se dialogové okno Nový projekt.

  3. V podokně Nainstalované šablony, zvolte možnost Visual Basic nebo Visual C# a pak v seznamu typů projektů zvolte možnost Aplikace WPF.

  4. Zadejte AsyncTracer jako název projektu a klikněte na tlačítko OK.

    V podokně Průzkumník řešení se zobrazí nový projekt.

  5. V editoru kódu sady Visual Studio zvolte kartu MainWindow.xaml.

    Pokud karta není zobrazena, otevřete místní nabídku souboru MainWindow.xaml v Průzkumníkovi řešení a vyberte možnost Zobrazit kód.

  6. V zobrazení XAML pro souboru MainWindow.xaml nahraďte kód následujícím kódem.

    <Window
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
        Title="Control Flow Trace" Height="350" Width="525">
        <Grid>
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top" Width="75"/>
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="510" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" d:LayoutOverrides="HorizontalMargin"/>
    
        </Grid>
    </Window>
    
    <Window
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="AsyncTracer.MainWindow"
            Title="Control Flow Trace" Height="350" Width="592">
        <Grid>
            <Button x:Name="startButton" Content="Start&#xa;" HorizontalAlignment="Left" Margin="250,10,0,0" VerticalAlignment="Top" Width="75" Height="24"  Click="startButton_Click" d:LayoutOverrides="GridBox"/>
            <TextBox x:Name="resultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="576" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" Grid.ColumnSpan="3"/>
        </Grid>
    </Window>
    

    Jednoduché okno obsahující textové pole a tlačítko se zobrazí v zobrazení Návrh souboru MainWindow.xaml.

  7. Přidejte odkaz pro System.Net.Http.

  8. V Průzkumníku řešení otevřete místní nabídku pro soubor MainWindow.xaml.vb nebo MainWindow.xaml.cs a pak zvolte možnost Zobrazit kód.

  9. Nahraďte kód v souborech MainWindow.xaml.vb nebo MainWindow.xaml.cs za následující kód.

    ' Add an Imports statement and a reference for System.Net.Http. 
    Imports System.Net.Http
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
    
            ' The display lines in the example lead you through the control shifts.
            ResultsTextBox.Text &= "ONE:   Entering StartButton_Click." & vbCrLf &
                "           Calling AccessTheWebAsync." & vbCrLf
    
            Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
    
            ResultsTextBox.Text &= vbCrLf & "FOUR:  Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is started." & vbCrLf &
                "           About to await getLengthTask -- no caller to return to." & vbCrLf
    
            Dim contentLength As Integer = Await getLengthTask
    
            ResultsTextBox.Text &= vbCrLf & "SIX:   Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is finished." & vbCrLf &
                "           Result from AccessTheWebAsync is stored in contentLength." & vbCrLf &
                "           About to display contentLength and exit." & vbCrLf
    
            ResultsTextBox.Text &=
                String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
        End Sub
    
    
        Async Function AccessTheWebAsync() As Task(Of Integer)
    
            ResultsTextBox.Text &= vbCrLf & "TWO:   Entering AccessTheWebAsync." 
    
            ' Declare an HttpClient object. 
            Dim client As HttpClient = New HttpClient()
    
            ResultsTextBox.Text &= vbCrLf & "           Calling HttpClient.GetStringAsync." & vbCrLf
    
            ' GetStringAsync returns a Task(Of String).  
            Dim getStringTask As Task(Of String) = client.GetStringAsync("https://msdn.microsoft.com")
    
            ResultsTextBox.Text &= vbCrLf & "THREE: Back in AccessTheWebAsync." & vbCrLf &
                "           Task getStringTask is started." 
    
            ' AccessTheWebAsync can continue to work until getStringTask is awaited.
    
            ResultsTextBox.Text &=
                vbCrLf & "           About to await getStringTask & return a Task(Of Integer) to StartButton_Click." & vbCrLf
    
            ' Retrieve the website contents when task is complete. 
            Dim urlContents As String = Await getStringTask
    
            ResultsTextBox.Text &= vbCrLf & "FIVE:  Back in AccessTheWebAsync." &
                vbCrLf & "           Task getStringTask is complete." &
                vbCrLf & "           Processing the return statement." &
                vbCrLf & "           Exiting from AccessTheWebAsync." & vbCrLf
    
            Return urlContents.Length
        End Function 
    
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    // Add a using directive and a reference for System.Net.Http; 
    using System.Net.Http;
    
    namespace AsyncTracer
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private async void startButton_Click(object sender, RoutedEventArgs e)
            {
                // The display lines in the example lead you through the control shifts.
                resultsTextBox.Text += "ONE:   Entering startButton_Click.\r\n" +
                    "           Calling AccessTheWebAsync.\r\n";
    
                Task<int> getLengthTask = AccessTheWebAsync();
    
                resultsTextBox.Text += "\r\nFOUR:  Back in startButton_Click.\r\n" +
                    "           Task getLengthTask is started.\r\n" +
                    "           About to await getLengthTask -- no caller to return to.\r\n";
    
                int contentLength = await getLengthTask;
    
                resultsTextBox.Text += "\r\nSIX:   Back in startButton_Click.\r\n" +
                    "           Task getLengthTask is finished.\r\n" +
                    "           Result from AccessTheWebAsync is stored in contentLength.\r\n" +
                    "           About to display contentLength and exit.\r\n";
    
                resultsTextBox.Text +=
                    String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
            }
    
    
            async Task<int> AccessTheWebAsync()
            {
                resultsTextBox.Text += "\r\nTWO:   Entering AccessTheWebAsync.";
    
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                resultsTextBox.Text += "\r\n           Calling HttpClient.GetStringAsync.\r\n";
    
                // GetStringAsync returns a Task<string>. 
                Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
    
                resultsTextBox.Text += "\r\nTHREE: Back in AccessTheWebAsync.\r\n" +
                    "           Task getStringTask is started.";
    
                // AccessTheWebAsync can continue to work until getStringTask is awaited.
    
                resultsTextBox.Text +=
                    "\r\n           About to await getStringTask and return a Task<int> to startButton_Click.\r\n";
    
                // Retrieve the website contents when task is complete. 
                string urlContents = await getStringTask;
    
                resultsTextBox.Text += "\r\nFIVE:  Back in AccessTheWebAsync." +
                    "\r\n           Task getStringTask is complete." +
                    "\r\n           Processing the return statement." +
                    "\r\n           Exiting from AccessTheWebAsync.\r\n";
    
                return urlContents.Length;
            }
        }
    }
    
  10. Zvolte klávesu F5 ke spuštění programu a pak zvolte tlačítko Start.

    Měl by se zobrazit následující výstup.

    ONE:   Entering startButton_Click.
               Calling AccessTheWebAsync.
    
    TWO:   Entering AccessTheWebAsync.
               Calling HttpClient.GetStringAsync.
    
    THREE: Back in AccessTheWebAsync.
               Task getStringTask is started.
               About to await getStringTask & return a Task<int> to startButton_Click.
    
    FOUR:  Back in startButton_Click.
               Task getLengthTask is started.
               About to await getLengthTask -- no caller to return to.
    
    FIVE:  Back in AccessTheWebAsync.
               Task getStringTask is complete.
               Processing the return statement.
               Exiting from AccessTheWebAsync.
    
    SIX:   Back in startButton_Click.
               Task getLengthTask is finished.
               Result from AccessTheWebAsync is stored in contentLength.
               About to display contentLength and exit.
    
    Length of the downloaded string: 33946.
    

Trasování programu

Kroky 1 a 2

První dva zobrazené řádky sledují cestu jako volání startButton_ClickAccessTheWebAsync a AccessTheWebAsync vyvolá asynchronní metoda HttpClientGetStringAsync(String).Následující obrázek popisuje volání z metody do metody.

Kroky 1 a 2

Návratový typ AccessTheWebAsync i client.GetStringAsync je Task.V případě AccessTheWebAsync je TResult celé číslo.Pro GetStringAsync je řetězec TResult.Další informace o asynchronních návratových typech metod naleznete zde: Asynchronní návratové typy (C# and Visual Basic).

Asynchronní metoda vracející úlohu vrátí instanci úlohy, když ovládací prvek přepne zpět na volajícího.Ovládací prvek vrátí asynchronní metodu jeho volajícímu buď pokud se střetne s operátorem Await nebo await ve volané metodě, nebo po ukončení volané metody.Zobrazené řádky, které jsou označovány "TŘI" až "ŠEST" trasují tuto část procesu.

Krok 3

V AccessTheWebAsync je asynchronní metoda GetStringAsync(String) volána ke stažení obsahu cílové webové stránky.Ovládací prvek se vrátí z client.GetStringAsync k AccessTheWebAsync při vrácení client.GetStringAsync.

Metoda client.GetStringAsync vrátí úlohu řetězce, která je přiřazena k proměnné getStringTask v AccessTheWebAsync.Následující řádek v příkladu programu ukazuje volání do client.GetStringAsync a přiřazení.

Dim getStringTask As Task(Of String) = client.GetStringAsync("https://msdn.microsoft.com")
Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");

Úlohy lze považovat za příslib od client.GetStringAsync ke konečnému vytvoření skutečného řetězce.Pokud má metoda AccessTheWebAsync mezitím činnost, která není závislá na přislíbeném řetězci z funkce client.GetStringAsync, můžete v této práci pokračovat během čekání funkce client.GetStringAsync.V příkladu představují následující řádky výstupu, které nesou označení „TŘI“, příležitost k nezávislé práci.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

Následující příkaz pozastaví průběh, když je očekáváno AccessTheWebAsync při getStringTask.

Dim urlContents As String = Await getStringTask
string urlContents = await getStringTask;

Následující obrázek znázorňuje tok řízení z client.GetStringAsync k přiřazení na getStringTask a z vytvoření getStringTask na použití operátoru await.

Krok 3

Výraz await pozastaví AccessTheWebAsync do návratu client.GetStringAsync.Mezitím se ovládací prvek vrátí volajícímu metody AccessTheWebAsync, startButton_Click.

[!POZNÁMKA]

Obvykle můžete očekávat volání asynchronní metody okamžitě.Jedno z následujících přiřazení například může nahradit předchozí vytvořený kód a poté očekává getStringTask:

  • Visual Basic: Dim urlContents As String = Await client.GetStringAsync("https://msdn.microsoft.com")

  • C#: string urlContents = await client.GetStringAsync("https://msdn.microsoft.com");

V tomto tématu je později použit operátor await pro výstupní řádky, které označí tok řízení programem.

Krok 4

Deklarovaný typ vrácení AccessTheWebAsync je Task(Of Integer) v jazyce Visual Basic a Task<int> v jazyce C#.Proto když je AccessTheWebAsync pozastaven, vrátí úkol čísla do startButton_Click.Je třeba si uvědomit, že vrácená úloha není getStringTask.Vrácený úkol je nový úkol celého čísla, které představuje to, co je ještě třeba provést v pozastavené metodě AccessTheWebAsync.Úkol je příslib z AccessTheWebAsync k vytvoření celého čísla po dokončení úkolu.

Následující příkaz přiřadí tuto úlohu proměnné getLengthTask.

Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
Task<int> getLengthTask = AccessTheWebAsync();

Jako v AccessTheWebAsync, startButton_Click může pokračovat v práci, která nezávisí na výsledcích asynchronní úlohy (getLengthTask), dokud je na úkol čekáno.Následující řádky výstupu představují tuto práci.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

V průběhu startButton_Click se pozastavuje, když se čeká na getLengthTask.Uvedený přiřazovací příkaz pozastaví startButton_Click do dokončení AccessTheWebAsync.

Dim contentLength As Integer = Await getLengthTask
int contentLength = await getLengthTask;

Na následujícím obrázku šipky zobrazují tok řízení z výrazu await v metodě AccessTheWebAsync k přiřazení hodnoty pro getLengthTask, následované běžným zpracováním v metodě startButton_Click, dokud není očekávána hodnota getLengthTask.

Krok 4

Krok 5

Když client.GetStringAsync oznamuje, že je dokončeno, zpracování v AccessTheWebAsync je uvolněno z pozastavení a může pokračovat po příkazu await.Následující řádky výstupu představují opětovné zpracování.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

Operanda příkazu return, urlContents.Length, je uložena v úloze, která vrátí AccessTheWebAsync.Výraz await získá tuto hodnotu z getLengthTask v startButton_Click.

Následující obrázek znázorňuje přenos ovládání po dokončení client.GetStringAsync (a getStringTask).

Krok 5

AccessTheWebAsync běží až do dokončení a řízení se vrátí do startButton_Click, které čeká na dokončení.

Krok 6

Když AccessTheWebAsync oznamuje, že je dokončeno, zpracování může pokračovat po příkazu await v startButton_Async.Program ve skutečnosti nemá žádnou další činnost k provedení.

Následující řádky výstupu představují opětovné zpracování ve startButton_Async:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Výraz Await se načte z celočíselné hodnoty getLengthTask, která je operandou příkazu return v AccessTheWebAsync.Následující příkaz přiřadí tuto hodnotu proměnné contentLength.

Dim contentLength As Integer = Await getLengthTask
int contentLength = await getLengthTask;

Následující obrázek znázorňuje návrat ovládacího prvku z AccessTheWebAsync do startButton_Click.

Krok 6

Viz také

Úkoly

Návod: Přístup k webu pomocí modifikátoru Async a operátoru Await (C# a Visual Basic)

Postupy: Použití ladicího programu u asynchronních metod

Koncepty

Asynchronní programování pomocí modifikátoru Async a operátoru Await (C# a Visual Basic)

Asynchronní návratové typy (C# and Visual Basic)

Další zdroje

Asynchronní vzorek: Tok řízení v asynchronních programech (C# a Visual Basic)