Annuler les tâches restantes lorsque l'une d'elles est terminée (C# et Visual Basic)

À l'aide de la méthode Task.WhenAny avec CancellationToken, vous pouvez annuler toutes les tâches restantes lorsqu'une tâche est terminée.La méthode d' WhenAny prend un argument qui est une collection de tâches.La méthode commence toutes les tâches et retourne une tâche unique.La tâche unique est complète lorsqu'une tâche dans la collection est terminée.

Cet exemple montre comment utiliser un jeton d'annulation conjointement avec WhenAny pour rester dans la première tâche de la collection de tâches et pour annuler les tâches restantes.Chaque tâche télécharge le contenu d'un site Web.L'exemple affiche la longueur du contenu du premier téléchargement se termine et annule les autres téléchargements.

[!REMARQUE]

Pour exécuter les exemples, vous devez avoir installé Visual Studio 2012 Visual Studio express 2012 pour le bureau Windows, ou .NET Framework 4,5 sur votre ordinateur.

Télécharger l'exemple

Vous pouvez télécharger le projet complet de Windows Presentation Foundation (WPF) de Exemple Async : Affiner votre application et puis suivre ces étapes.

  1. Décompressez le fichier que vous avez téléchargé, puis démarrez ensuite Visual Studio 2012.

  2. Dans la barre de menus, sélectionnez Fichier, Ouvrir, Projet/Solution.

  3. Dans la boîte de dialogue Ouvrir un projet, ouvrez le dossier qui contient l'exemple de code que vous avez décompressé, puis ouvre le fichier solution (.sln) pour AsyncFineTuningCS ou AsyncFineTuningVB.

  4. Dans Explorateur de solutions, ouvrez le menu contextuel CancelAfterOneTask , puis choisissez Définir comme projet de démarrage.

  5. Choisissez la touche F5 pour exécuter le projet.

    Choisissez les clés Ctrl+F5 pour exécuter le projet sans le déboguer.

  6. Exécutez le programme plusieurs fois pour vérifier que des téléchargements finissent en premier.

Si vous ne souhaitez pas télécharger le projet, vous pouvez examiner les fichiers de MainWindow.xaml.vb ou MainWindow.xaml.cs à la fin de cette rubrique.

Création de l'exemple

L'exemple de cette rubrique ajoute au projet développé dans Annuler une tâche ou une liste de tâches (C# et Visual Basic) pour annuler une liste de tâches.L'exemple utilise la même interface, bien que le bouton Annuler n'est pas utilisé explicitement.

Pour générer l'exemple vous-même, pas - à - pas, suivez les instructions dans la section « exemple » téléchargement, mais choisissez CancelAListOfTasks comme Projet de départ.Ajoutez les modifications dans cette rubrique à ce projet.

Dans le fichier de MainWindow.xaml.vb ou MainWindow.xaml.cs du projet de CancelAListOfTasks, démarrez le passage par déplacer les étapes de traitement pour chaque site Web à partir de la boucle dans AccessTheWebAsync à la méthode suivante async.

' ***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
// ***Bundle the processing steps for a website into one async method.
async Task<int> ProcessURLAsync(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;
}

Dans AccessTheWebAsync, cet exemple utilise une requête, la méthode d' ToArray<TSource>, et la méthode d' WhenAny pour créer et lancer un tableau des tâches.L'application de WhenAny au tableau retourne une tâche unique qui, une fois attendue, évalue la premiere tâche complétée dans le tableau de tâches.

Apportez les modifications suivantes dans AccessTheWebAsync.Les astérisques marquent les modifications du fichier de code.

  1. Commentez ou supprimez la boucle.

  2. Créez une requête qui, lorsqu'elle est exécutée, génère une collection de tâches génériques.Chaque appel à ProcessURLAsync retourne Task<TResult> où TResult est un entier.

    ' ***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)
    
    // ***Create a query that, when executed, returns a collection of tasks.
    IEnumerable<Task<int>> downloadTasksQuery =
        from url in urlList select ProcessURLAsync(url, client, ct);
    
  3. Appelez ToArray pour exécuter la requête et lancer des tâches.L'application de la méthode WhenAny dans l'étape suivante exécuterait la requête et commencerait les tâches sans utiliser ToArray, mais d'autres méthodes ne peuvent pas.La pratique la plus sûre consiste à forcer l'exécution de la requête explicitement.

    ' ***Use ToArray to execute the query and start the download tasks. 
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
    // ***Use ToArray to execute the query and start the download tasks. 
    Task<int>[] downloadTasks = downloadTasksQuery.ToArray();
    
  4. Appelez WhenAny sur la collection de tâches.WhenAny retourne Task(Of Task(Of Integer)) ou Task<Task<int>>.Autrement dit, WhenAny retourne une tâche qui correspond à une Task(Of Integer) unique ou à Task<int> lorsqu'il a attendu.Que la tâche unique est la première tâche dans la collection à se terminer.La tâche qui a terminé d'abord est assignée à firstFinishedTask.Le type de firstFinishedTask est Task<TResult> où TResult est un entier car il s'agit du type de retour d' ProcessURLAsync.

    ' ***Call WhenAny and then await the result. The task that finishes 
    ' first is assigned to firstFinishedTask.
    Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
    
    // ***Call WhenAny and then await the result. The task that finishes 
    // first is assigned to firstFinishedTask.
    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
    
  5. Dans cet exemple, vous vous intéressez uniquement dans la tâche qui se termine en premier.Par conséquent, utilisez CancellationTokenSource.Cancel pour annuler les tâches restantes.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
    // ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel();
    
  6. Enfin, attendez firstFinishedTask pour récupérer la longueur du contenu téléchargé.

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

Exécutez le programme plusieurs fois pour vérifier que des téléchargements finissent en premier.

Exemple complet

Le code suivant est le fichier complet de MainWindow.xaml.vb ou MainWindow.xaml.cs pour l'exemple.Les astérisques marquent les éléments ajoutés pour cet exemple.

Notez que vous devez ajouter une référence pour System.Net.Http.

Vous pouvez télécharger le projet depuis Exemple Async : Affiner votre application.

' 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 & "Download complete."

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

        Catch ex As Exception
            resultsTextBox.Text &= vbCrLf & "Download 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()

        '' Comment out or delete the loop.
        ''For Each url In urlList
        ''    ' GetAsync returns a Task(Of HttpResponseMessage). 
        ''    ' Argument ct carries the message if the Cancel button is chosen. 
        ''    ' Note that the Cancel button can cancel all remaining downloads.
        ''    Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        ''    resultsTextBox.Text &=
        ''        String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, urlContents.Length)
        ''Next

        ' ***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 ToArray to execute the query and start the download tasks. 
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' ***Call WhenAny and then await the result. The task that finishes 
        ' first is assigned to firstFinishedTask.
        Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

        ' ***Cancel the rest of the downloads. You just want the first one.
        cts.Cancel()

        ' ***Await the first completed task and display the results
        ' Run the program several times to demonstrate that different
        ' websites can finish first.
        Dim length = Await firstFinishedTask
        resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)
    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 downloaded website:  158856

' Download 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 CancelAfterOneTask
{
    public partial class MainWindow : Window
    {
        // Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }


        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();

            resultsTextBox.Clear();

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

            // Set the CancellationTokenSource to null when the download is complete.
            cts = null;
        }


        // You can still include a Cancel button if you want to.
        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
        }


        // Provide a parameter for the CancellationToken.
        async Task AccessTheWebAsync(CancellationToken ct)
        {
            HttpClient client = new HttpClient();

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

            // ***Comment out or delete the loop.
            //foreach (var url in urlList)
            //{
            //    // GetAsync returns a Task<HttpResponseMessage>. 
            //    // Argument ct carries the message if the Cancel button is chosen. 
            //    // ***Note that the Cancel button can cancel all remaining downloads.
            //    HttpResponseMessage response = await client.GetAsync(url, ct);

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

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

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

            // ***Use ToArray to execute the query and start the download tasks. 
            Task<int>[] downloadTasks = downloadTasksQuery.ToArray();

            // ***Call WhenAny and then await the result. The task that finishes 
            // first is assigned to firstFinishedTask.
            Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);

            // ***Cancel the rest of the downloads. You just want the first one.
            cts.Cancel();

            // ***Await the first completed task and display the results. 
            // Run the program several times to demonstrate that different
            // websites can finish first.
            var length = await firstFinishedTask;
            resultsTextBox.Text += String.Format("\r\nLength of the downloaded website:  {0}\r\n", length);
        }


        // ***Bundle the processing steps for a website into one async method.
        async Task<int> ProcessURLAsync(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;
        }


        // Add a method that creates a list of web addresses.
        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "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;
        }
    }
    // Sample output:

    // Length of the downloaded website:  158856

    // Download complete.
}

Voir aussi

Référence

WhenAny

Concepts

Réglage de votre application Async (C# et Visual Basic)

Programmation asynchrone avec Async et Await (C# et Visual Basic)

Autres ressources

Exemple Async : Affiner votre application