共用方式為


啟動多項工作並在它們完成時進行處理 (C# 和 Visual Basic)

您可以使用 Task.WhenAny,您可以同時啟動多個工作並當它們完成時再一個一個處理它們,而非依照所啟動的順序處理它們。

下列範例會使用查詢建立工作的集合。每個工作下載指定之網站的內容。在每個 while 迴圈,在 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. 在 [方案總管],開啟 [ProcessTasksAsTheyFinish] 專案的捷徑功能表,然後選取 [設定為啟始專案]。

  5. 選擇 F5 鍵執行專案。

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

  6. 執行專案幾次驗證下載的長度不相同順序永遠會出現。

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

建置範例

這個範例會將加入至 當其中一項工作完成時,取消剩餘的工作 (C# 和 Visual Basic) 所開發的程式碼並使用相同的 UI。

若要建置範例,逐步,請依照 < 下載範例中的」表示部分,不過,選取 [CancelAfterOneTask] 為 [啟始專案]。將在本主題中的變更該專案的 AccessTheWebAsync 方法。變更標記以星號。

[CancelAfterOneTask] 專案已經包含,,當實作時,建立工作集合的查詢。為 ProcessURLAsync 的每個呼叫都會在下列程式碼會傳回 TResultTask<TResult> 為整數。

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

在專案的 MainWindow.xaml.vb 或 MainWindow.xaml.cs 檔案,對 AccessTheWebAsync 方法的下列變更。

  • 藉由套用 Enumerable.ToList<TSource> 執行查詢而不是 ToArray<TSource>

    Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
    
    List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
    
  • 將集合執行每項工作的下列步驟的 while 迴圈。

    1. 等候呼叫 WhenAny 來識別集合中的第一個工作以完成下載。

      Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
      
      Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
      
    2. 從集合中移除該工作。

      downloadTasks.Remove(firstFinishedTask)
      
      downloadTasks.Remove(firstFinishedTask);
      
    3. 等候 firstFinishedTask,這是由 ProcessURLAsync的呼叫傳回。firstFinishedTask 變數是 TReturn 是整數的 Task<TResult> 。工作已經完成,不過,您需要等候它來擷取下載之網站的長度,如下列範例所示。

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

您應該執行專案幾次驗證下載的長度不相同順序永遠會出現。

注意事項警告

您在迴圈中使用 WhenAny ,如本範例所說明,解決涉及少量工作的問題。不過,因此,如果您需要處理,大量工作其他方法會更有效率。如需詳細資訊和範例,請 管理工作,這些工作完成參閱。

完整範例

下列程式碼是 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 & "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.

請參閱

參考

WhenAny

概念

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

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

其他資源

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