共用方式為


當其中一項工作完成時,取消剩餘的工作 (C# 和 Visual Basic)

藉由使用 Task.WhenAny方法和CancellationToken一起,當一個工作完成後,您可以取消所有剩下的工作。 WhenAny 方法會使用工作集合的引數。 方法會啟動所有工作並傳回單一工作。 表示集合中的所有工作完成時,單一工作完成。

這個範例展示如何使用消除標誌結合WhenAny 來保留第一個工作讓其從工作的集合中結束,並取消剩下的工作。 每個工作下載網站的內容。 這個範例顯示第下載內容的長度完成並移除其他下載。

注意事項注意事項

若要執行此範例,您必須將 Visual Studio 2012 中, Windows 桌面上的 Visual Studio Express 2012 for或者在您的電腦上安裝 .NET Framework 4.5。

下載範例

您可以下載完成 Windows Presentation Foundation (WPF) 專案從 Async 範例:微調應用程式 中依照下列步驟執行。

  1. 解壓縮您下載的檔案,然後啟動 Visual Studio 2012。

  2. 在功能表列上,選擇 [檔案]、[開啟]、[專案/方案]。

  3. 在 [開啟專案] 對話方塊中,開啟含有範例程式碼會在解壓縮,然後開啟 AsyncFineTuningCS 或 AsyncFineTuningVB 方案的資料夾 (.sln) 檔。

  4. 在 [方案總管],開啟 [CancelAfterOneTask] 專案的捷徑功能表,然後選取 [設定為啟始專案]。

  5. 選擇 F5 鍵執行專案。

    選取 Ctrl+F5 鍵執行專案,但不偵錯。

  6. 執行程式幾次驗證不同下載先完成。

如果您不要下載專案,您可以檢閱 MainWindow.xaml.vb 或 MainWindow.xaml.cs 檔案在這個主題的結尾。

建置範例

本主題中的範例會加入至 取消一項或多項工作 (C# 和 Visual Basic) 中開發取消工作清單中的專案。 這個範例使用同一個使用者介面,雖然 取消 鈕不需要依特定方式使用。

若要建置範例,逐步,請依照 < 下載範例中的」表示部分,不過,選取 [CancelAListOfTasks] 為 [啟始專案]。 將在本主題中的變更加入至該專案。

在 [CancelAListOfTasks] 專案的 MainWindow.xaml.vb 或 MainWindow.xaml.cs 檔案中,藉由移動每個網站的處理步驟開始轉換從 AccessTheWebAsync 中的迴圈與下列非同步方法。

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

在 AccessTheWebAsync中,範例使用查詢、 ToArray<TSource> 方法或 WhenAny 方法建立並啟動工作。 WhenAny 應用到陣列上會回傳一個單一的工作,當等待時,會確保工作陣列中的第一個工作會完成。

在 AccessTheWebAsync進行下列變更。 星號標示出在程式碼檔案中的變更。

  1. 標記為註解或刪除迴圈。

  2. 建立一個當執行時會產生泛型工作集合的查詢。 對 ProcessURLAsync 的每一個呼叫傳回 TResult 是整數的 Task<TResult>

    ' ***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. 呼叫 ToArray 來執行查詢並啟動工作。 在下一個步驟中應用WhenAny 方法將會執行查詢並啟動工作,而不需要使用 ToArray,不過,其他方法可能不是如此。 最安全的做法是明確強制查詢的執行。

    ' ***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. 在工作的集合上呼叫 WhenAny 。 WhenAny傳回一個Task(Of Task(Of Integer))或Task<Task<int>> 。 也就是說 WhenAny 傳回等同於一個單一 Task(Of Integer) 或 Task<int> 的工作,在等候時。 這個工作是集合中的第一個完成的工作。 您必須先完成的工作指派給 firstFinishedTask。 firstFinishedTask 的型別是 Task<TResult>,然而 TResult是一個整數因為那是 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. 在此範例中,您只需要專注在先完成的工作上。 因此,請使用 CancellationTokenSource.Cancel 來取消剩餘工作。

    ' ***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. 最後,請等候 firstFinishedTask 擷取下載內容的長度。

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

執行程式幾次驗證不同下載先完成。

完整範例

下列程式碼完成 MainWindow.xaml.vb 或範例的 MainWindow.xaml.cs 檔案。 加入星號標記為這個範例加入的項目。

請注意您必須增加 System.Net.Http的參考。

您可以從 Async範例:微調您的應用程式下載這個專案。

' 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.
}

請參閱

參考

WhenAny

概念

微調非同步應用程式 (C# 和 Visual Basic)

使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic)

其他資源

Async範例:微調您的應用程式