Sdílet prostřednictvím


Zahájení více úloh s modifikátorem Async a jejich zpracování po dokončení (C# a Visual Basic)

Pomocí příkazu Task.WhenAny můžete spustit více úkolů současně a zpracovat je postupně tak, jak jsou dokončeny (namísto zpracování v pořadí, ve kterém jste je spustili).

Následující příklad používá dotaz k vytvoření kolekce úkolů.Každý úkol umožňuje stažení obsahu ze zadaného webu.V každé iteraci cyklu while vrátí očekávané volání metody WhenAny úlohu v kolekci úloh, která dokončí stahování jako první.Tento úkol je odebrán z kolekce a zpracování.Smyčka se opakuje dokud kolekce neobsahuje žádné další úlohy.

[!POZNÁMKA]

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

Stažení příkladu

Můžete si stáhnout kompletní projekt Windows Presentation Foundation (WPF) z části Asynchronní vzorek: Doladění aplikace a pak provést tyto kroky.

  1. Dekomprimujte 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. V dialogovém okně Otevřít projekt otevřete složku, která obsahuje zdrojový k=od, který jste dekomprimovali, a pak otevřete soubor řešení (.sln) pro AsyncFineTuningCS nebo AsyncFineTuningVB.

  4. Průzkumníku řešení otevřete místní nabídku projektu ProcessTasksAsTheyFinish a vyberte položku Nastavit jako položku po spuštění.

  5. Spusťte projekt tlačítkem F5.

    Vyberte klávesy Ctrl+F5 ke spuštění projektu bez ladění.

  6. Projekt několikrát spusťte a ověřte tak, že stažené délky se vždy nezobrazí ve stejném pořadí.

Pokud nechcete stáhnout projekt, můžete zkontrolovat soubory MainWindow.xaml.vb a MainWindow.xaml.cs na konci tohoto tématu.

Sestavení příkladu

Tento příklad se přidá ke kód, který je napsán v jazyce Zrušení zbývajících asynchronních úloh po dokončení jedné z nich (C# a Visual Basic) a používá stejné uživatelské rozhraní.

Chcete-li vytvořit příklad sami krok za krokem, postupujte podle pokynů v oddíle "Stahování příkladu", ale zvolte CancelAfterOneTask jako možnost Projekt po spuštění.Přidejte změny v tomto tématu do metody AccessTheWebAsync daného projektu.Změny jsou označeny hvězdičkami.

Projekt CancelAfterOneTask již obsahuje dotaz, který při spuštění vytvoří kolekci úkolů.Každé volání do ProcessURLAsync v následujícím kódu vrátí Task, kde je TResult celé číslo.

Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
    From url In urlList Select ProcessURLAsync(url, client, ct)
IEnumerable<Task<int>> downloadTasksQuery =
    from url in urlList select ProcessURL(url, client, ct);

V souboru MainWindow.xaml.vb nebo MainWindow.xaml.cs daného projektu proveďte následující změny metody AccessTheWebAsync.

  • Spusťte dotaz s použitím Enumerable.ToList``1 namísto ToArray``1.

    Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
    
    List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
    
  • Přidejte smyčku while, která provede následující kroky pro každý úkol v kolekci.

    1. Čeká volání WhenAny za účelem identifikace prvního úkolu v kolekci, který dokončí stažení.

      Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
      
      Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
      
    2. Odebere tento úkol z kolekce.

      downloadTasks.Remove(firstFinishedTask)
      
      downloadTasks.Remove(firstFinishedTask);
      
    3. Čeká firstFinishedTask, který je vrácen voláním ProcessURLAsync.Proměnná firstFinishedTask je Task, kde TReturn je celé číslo.Úkol je již dokončen, ale můžete od něj očekávat, že načte délku staženého webu, jak ukazuje následující příklad.

      Dim length = Await firstFinishedTask
      resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
      
      int length = await firstFinishedTask;
      resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
      

Projekt byste měli několikrát spustit a ověřit tak, že stažené délky se vždy nezobrazí ve stejném pořadí.

Poznámka k upozorněníUpozornění

Můžete použít WhenAny ve smyčce, jak je popsáno v příkladu, k vyřešení problémů, které se týkají malého počtu úkolů.Další postupy jsou však efektivnější, pokud máte velký počet úkolů zpracovat.Další informace a příklady naleznete v tématu Zpracování úkolů při jejich dokončování.

Kompletní příklad

Následující kód je celý text souboru MainWindow.xaml.vb nebo MainWindow.xaml.cs pro příklad.Hvězdičky označují prvky, které byly přidány pro tento příklad.

Všimněte si, že je nutné přidat odkaz pro System.Net.Http.

Projekt si můžete stáhnout na stránkách Asynchronní vzorek: Jemné ladění aplikace.

' Add an Imports directive and a reference for System.Net.Http. 
Imports System.Net.Http

' Add the following Imports directive for System.Threading. 
Imports System.Threading

Class MainWindow

    ' Declare a System.Threading.CancellationTokenSource. 
    Dim cts As CancellationTokenSource


    Private Async Sub startButton_Click(sender As Object, e As RoutedEventArgs)

        ' Instantiate the CancellationTokenSource.
        cts = New CancellationTokenSource()

        resultsTextBox.Clear()

        Try
            Await AccessTheWebAsync(cts.Token)
            resultsTextBox.Text &= vbCrLf & "Downloads complete." 

        Catch ex As OperationCanceledException
            resultsTextBox.Text &= vbCrLf & "Downloads canceled." & vbCrLf

        Catch ex As Exception
            resultsTextBox.Text &= vbCrLf & "Downloads failed." & vbCrLf
        End Try 

        ' Set the CancellationTokenSource to Nothing when the download is complete.
        cts = Nothing 
    End Sub 


    ' You can still include a Cancel button if you want to. 
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)

        If cts IsNot Nothing Then
            cts.Cancel()
        End If 
    End Sub 


    ' Provide a parameter for the CancellationToken. 
    ' Change the return type to Task because the method has no return statement.
    Async Function AccessTheWebAsync(ct As CancellationToken) As Task

        Dim client As HttpClient = New HttpClient()

        ' Call SetUpURLList to make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        ' ***Create a query that, when executed, returns a collection of tasks. 
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client, ct)

        ' ***Use ToList to execute the query and start the download tasks.  
        Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()

        ' ***Add a loop to process the tasks one at a time until none remain. 
        While downloadTasks.Count > 0
            ' ***Identify the first task that completes. 
            Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

            ' ***Remove the selected task from the list so that you don't 
            ' process it more than once.
            downloadTasks.Remove(firstFinishedTask)

            ' ***Await the first completed task and display the results. 
            Dim length = Await firstFinishedTask
            resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
        End While 

    End Function 


    ' Bundle the processing steps for a website into one async method.
    Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

        ' GetAsync returns a Task(Of HttpResponseMessage).  
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' Retrieve the website contents from the HttpResponseMessage. 
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        Return urlContents.Length
    End Function 


    ' Add a method that creates a list of web addresses. 
    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 

End Class 


' Sample output: 

' Length of the download:  226093 
' Length of the download:  412588 
' Length of the download:  175490 
' Length of the download:  204890 
' Length of the download:  158855 
' Length of the download:  145790 
' Length of the download:  44908 
' Downloads complete.
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;

// Add the following using directive. 
using System.Threading;


namespace ProcessTasksAsTheyFinish
{
    public partial class MainWindow : Window
    {
        // Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();

            try
            {
                await AccessTheWebAsync(cts.Token);
                resultsTextBox.Text += "\r\nDownloads complete.";
            }
            catch (OperationCanceledException)
            {
                resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
            }
            catch (Exception)
            {
                resultsTextBox.Text += "\r\nDownloads failed.\r\n";
            }

            cts = null;
        }


        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
        }


        async Task AccessTheWebAsync(CancellationToken ct)
        {
            HttpClient client = new HttpClient();

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            // ***Create a query that, when executed, returns a collection of tasks.
            IEnumerable<Task<int>> downloadTasksQuery =
                from url in urlList select ProcessURL(url, client, ct);

            // ***Use ToList to execute the query and start the tasks. 
            List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

            // ***Add a loop to process the tasks one at a time until none remain. 
            while (downloadTasks.Count > 0)
            {
                    // Identify the first task that completes.
                    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);

                    // ***Remove the selected task from the list so that you don't 
                    // process it more than once.
                    downloadTasks.Remove(firstFinishedTask);

                    // Await the completed task. 
                    int length = await firstFinishedTask;
                    resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
            }
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
        {
            // GetAsync returns a Task<HttpResponseMessage>. 
            HttpResponseMessage response = await client.GetAsync(url, ct);

            // Retrieve the website contents from the HttpResponseMessage. 
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

            return urlContents.Length;
        }
    }
}

// Sample Output: 

// Length of the download:  226093 
// Length of the download:  412588 
// Length of the download:  175490 
// Length of the download:  204890 
// Length of the download:  158855 
// Length of the download:  145790 
// Length of the download:  44908 
// Downloads complete.

Viz také

Referenční dokumentace

WhenAny``1

Koncepty

Vyladění aplikace s modifikátorem Async (C# a Visual Basic)

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

Další zdroje

Asynchronní vzorek: Jemné ladění aplikace