完了時での複数の同期タスクとプロセスの実行 (C# および Visual Basic)
Task.WhenAny を使用すると、複数のタスクを、開始された順番に処理するのでなく、同時に開始して完了するごとに 1 つずつ処理できます。
クエリを使用して、タスクのコレクションを作成する例を次に示します。 各タスクは、指定された Web サイトのコンテンツをダウンロードします。 while ループの各反復で、待機されている WhenAny への呼び出しは、最初にダウンロードを終了するタスクのコレクションにあるタスクを返します。 タスクはコレクションから削除され、処理されます。 ループは、コレクションのタスクがなくなるまで繰り返されます。
注意
この例を実行するには、Visual Studio 2012、Visual Studio 2013、Visual Studio Express 2012 for Windows Desktop、Visual Studio Express 2013 for Windows、または .NET Framework 4.5 か 4.5.1 がコンピューターにインストールされている必要があります。
例をダウンロードする
完全な Windows Presentation Foundation (WPF) プロジェクトは「Async Sample: Fine Tuning Your Application (非同期のサンプル: アプリケーションの微調整)」からダウンロードできます。次の手順に従います。
ダウンロードしたファイルを圧縮解除し、Visual Studio を起動します。
メニュー バーで [ファイル]、[開く]、[プロジェクト/ソリューション] の順に選択します。
[プロジェクトを開く] のダイアログ ボックスで、圧縮解除したサンプル コードを含むフォルダーを開き、AsyncFineTuningCS または AsyncFineTuningVB のソリューション (.sln) ファイルを開きます。
ソリューション エクスプローラーで、ProcessTasksAsTheyFinish プロジェクトのショートカット メニューを開き、[スタートアップ プロジェクトに設定] をクリックします。
F5 キーを押してプロジェクトを実行します。
Ctrl + F5 キーを押して、デバッグを行わずにプロジェクトを実行します。
ダウンロードの長さが常に同じ順序では表示されないことを確認するために、プロジェクトを複数回実行します。
プロジェクトをダウンロードしない場合は、このトピックの最後の MainWindow.xaml.vb または MainWindow.xaml.cs ファイルを使用できます。
例のビルド
この例では、完了後の残りの非同期タスクのキャンセル (C# および Visual Basic) で開発したコードを追加し、同じ UI を使用します。
この例を自分でビルドするには、「例をダウンロードする」のセクションの詳細な手順の指示に従いますが、[スタートアップ プロジェクト] として CancelAfterOneTask を選択します。 そのプロジェクトの AccessTheWebAsync メソッドに、このトピックでの変更を追加します。 変更部分にはアスタリスクが付いています。
CancelAfterOneTask プロジェクトには、実行時にタスクのコレクションを作成するクエリが含まれています。 次のコードの ProcessURLAsync への各呼び出しは、TResult が整数である Task を返します。
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 メソッドに次の変更を行います。
ToArray``1 の代わりに Enumerable.ToList``1 を適用して、クエリを実行します。
Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
コレクションの各タスクで次の手順を実行する while ループを追加します。
WhenAny への呼び出しを待機し、ダウンロードを終了する、コレクションの最初のタスクを識別します。
Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
コレクションからそのタスクを削除します。
downloadTasks.Remove(firstFinishedTask)
downloadTasks.Remove(firstFinishedTask);
ProcessURLAsync への呼び出しから返される、firstFinishedTask を待機します。 firstFinishedTask 変数は TReturn が整数である Task です。 次の例に示すように、タスクは既に完了していますが、ダウンロードした Web サイトの長さの取得を待機します。
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 を使って、例に示すように、いくつかのタスクを格納する問題を解決できます。ただし、多数のタスクが処理する場合、他のアプローチがより効率的です。詳細と例については、「Processing tasks as they complete (完了したタスクを処理する)」を参照してください。
コード例全体
次のコードは、この例での MainWindow.xaml.vb または MainWindow.xaml.cs ファイルのテキスト全体です。 アスタリスクはこの例のために追加された要素を示しています。
System.Net.Http の参照を追加する必要があることに注意してください。
このプロジェクトは Async Sample: Fine Tuning Your 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 & "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.
参照
関連項目
概念
非同期アプリケーションの微調整 (C# および Visual Basic)
Async および Await を使用した非同期プログラミング (C# および Visual Basic)
その他の技術情報
Async Sample: Fine Tuning Your Application (非同期のサンプル: アプリケーションの微調整)