Sdílet prostřednictvím


Zrušení zbývajících asynchronních úloh po dokončení jedné z nich (C# a Visual Basic)

Pomocí metody Task.WhenAny spolu s hodnotou CancellationToken můžete po dokončení jednoho úkolu zrušit všechny zbývající úkoly.Metoda WhenAny přebírá argument, který je sadou úkolů.Metoda spustí všechny úlohy a vrátí jeden úkol.Jedna úloha je dokončena po dokončení libovolné úlohy v kolekci.

Tento příklad ukazuje, jak použít token zrušení společně s WhenAny pro udržení prvního úkol k dokončení z kolekce úkolů a zrušení zbývajících úkolů.Každý úkol umožňuje stažení obsahu z webu.Příklad zobrazuje délku obsahu prvního stahování pro dokončení a zruší ostatní stahování.

[!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 CancelAfterOneTask a vyberte položku Nastavit jako projekt 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. Spusťte program několikrát, chcete-li se ujistit, že budou nejprve dokončena různá stahování.

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

Sestavení příkladu

Příklad v tomto tématu je přidán do projektu, který byl vyvinut v Zrušení asynchronní úlohy nebo seznamu úloh (C# a Visual Basic), aby byl zrušen seznam úkolů.V příkladu se používá stejné uživatelské rozhraní, i když tlačítko Zrušit není použito explicitně.

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

V souboru MainWindow.xaml.vb nebo MainWindow.xaml.cs projektu CancelAListOfTasks zahajte přechod přesunutím kroků zpracování pro každý web ze smyčky v metodě AccessTheWebAsync do následující asynchronní metody.

' ***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;
}

V AccessTheWebAsync používá tento příklad dotaz, metodu ToArray``1 a metodu WhenAny k vytvoření a spuštění pole úloh.Použití WhenAny na pole vrátí jeden úkol, který je-li očekávaný, vyhodnotí jako první úkol k dosažení dokončení v poli úloh.

Proveďte následující změny v AccessTheWebAsync.Hvězdičky označují změny v souboru kódu.

  1. Okomentujte nebo odstraňte smyčku.

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

    ' ***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. Voláním ToArray proveďte dotaz a spusťte úlohy.Použití metody WhenAny v dalším kroku by spustilo dotaz a spustilo úlohu bez použití ToArray, ale jiné metody nemusí.Nejbezpečnější metodou je vynutit spuštění dotazu explicitně.

    ' ***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. Volání WhenAny v kolekci úkolů.WhenAny vrátí hodnotu Task(Of Task(Of Integer)) nebo Task<Task<int>>. To znamená, že WhenAny vrátí úkol, jehož výsledkem je jediné Task(Of Integer) nebo Task<int>, pokus je očekáváno.Jeden úkol je první úkol v kolekci pro dokončení.Úkol, který skončil první, je přiřazen firstFinishedTask.Typ firstFinishedTask je Task, kde TResult je celé číslo, protože to je návratový typ 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. V tomto příkladu se zajímáte pouze úlohu, která je dokončena první.Proto používejte CancellationTokenSource.Cancel, chcete-li zrušit zbývající úkoly.

    ' ***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. Nakonec vyčkejte, než firstFinishedTask načte délku stahovaného obsahu.

    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);
    

Spusťte program několikrát, chcete-li se ujistit, že budou nejprve dokončena různá stahování.

Kompletní příklad

Následující kód je celý soubor 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 & "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.
}

Viz také

Referenční dokumentace

WhenAny

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