タスクの一覧を取り消す
非同期のコンソール アプリケーションは、終了まで待機しない場合にキャンセルすることができます。 このトピックの例に従うと、Web サイトの一覧のコンテンツをダウンロードするアプリケーションにキャンセルを追加できます。 各タスクに CancellationTokenSource インスタンスを関連付けると、多くのタスクをキャンセルすることができます。 Enter キーを選択すると、完了していないすべてのタスクがキャンセルされます。
このチュートリアルの内容:
- .NET コンソール アプリケーションの作成
- キャンセルをサポートする非同期アプリケーションの作成
- キャンセル通知のデモンストレーション
前提条件
このチュートリアルには、次のものが必要です。
サンプル アプリケーションの作成
新しい .NET Core コンソール アプリケーションを作成します。 dotnet new console
コマンドを使うか、Visual Studio から作成できます。 任意のコード エディターで Program.cs ファイルを開きます。
using ステートメントの置換
既存の using ステートメントを次の宣言に置き換えます。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
フィールドを追加する
Program
クラス定義で、次の 3 つのフィールドを追加します。
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
static readonly IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dynamics365",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://learn.microsoft.com/system-center",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/maui"
};
CancellationTokenSource は、要求されるキャンセルを CancellationToken に通知するために使用されます。 HttpClient
では、HTTP 要求を送信して HTTP 応答を受信する機能が公開されます。 s_urlList
には、このアプリケーションで処理を計画するすべての URL が格納されます。
アプリケーション エントリ ポイントの更新
コンソール アプリケーションのメイン エントリ ポイントは、Main
メソッドです。 既存のメソッドを以下に置き換えます。
static async Task Main()
{
Console.WriteLine("Application started.");
Console.WriteLine("Press the ENTER key to cancel...\n");
Task cancelTask = Task.Run(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Console.WriteLine("Press the ENTER key to cancel...");
}
Console.WriteLine("\nENTER key pressed: cancelling downloads.\n");
s_cts.Cancel();
});
Task sumPageSizesTask = SumPageSizesAsync();
Task finishedTask = await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });
if (finishedTask == cancelTask)
{
// wait for the cancellation to take place:
try
{
await sumPageSizesTask;
Console.WriteLine("Download task completed before cancel request was processed.");
}
catch (TaskCanceledException)
{
Console.WriteLine("Download task has been cancelled.");
}
}
Console.WriteLine("Application ending.");
}
更新した Main
メソッドは、async main と見なされるようになります。これにより、実行可能ファイルへの非同期エントリ ポイントが可能になります。 いくつかの指示メッセージがコンソールに出力され、次に cancelTask
という名前の Task インスタンスが宣言されます。これにより、コンソールのキー ストロークが読み取られるようになります。 Enter キーが押されると、CancellationTokenSource.Cancel() の呼び出しが行われます。 これにより、キャンセルが通知されるようになります。 次に、sumPageSizesTask
変数が SumPageSizesAsync
メソッドから割り当てられています。 両方のタスクは次に Task.WhenAny(Task[]) に渡され、2 つのタスクのいずれかが完了したときに続行されるようになります。
コードの次のブロックは、キャンセルが処理されるまで、アプリケーションが終了しないようにします。 最初に完了するタスクが cancelTask
の場合、sumPageSizeTask
は待機されます。 キャンセルされた場合、待機すると System.Threading.Tasks.TaskCanceledException がスローされます。 ブロックはその例外をキャッチし、メッセージを出力します。
非同期の合計ページ サイズ メソッドの作成
Main
メソッドの下に、SumPageSizesAsync
メソッドを追加します。
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
このメソッドを開始するには、Stopwatch をインスタンス化して開始します。 次に、s_urlList
内の各 URL をループし、ProcessUrlAsync
を呼び出します。 反復処理のたびに、s_cts.Token
が ProcessUrlAsync
メソッドに渡され、コードから Task<TResult> が返されます。TResult
は整数です。
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
プロセス メソッドの追加
SumPageSizesAsync
メソッドの下に次の ProcessUrlAsync
メソッドを追加します。
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
どの URL に対しても、このメソッドにより、提供される client
インスタンスが使用され、応答が byte[]
として取得されます。 CancellationToken インスタンスは、HttpClient.GetAsync(String, CancellationToken) および HttpContent.ReadAsByteArrayAsync() メソッドに渡されます。 token
は、要求されるキャンセルの登録に使用されます。 URL と長さがコンソールに出力された後、長さが返されます。
アプリケーション出力の例
Application started.
Press the ENTER key to cancel...
https://learn.microsoft.com 37,357
https://learn.microsoft.com/aspnet/core 85,589
https://learn.microsoft.com/azure 398,939
https://learn.microsoft.com/azure/devops 73,663
https://learn.microsoft.com/dotnet 67,452
https://learn.microsoft.com/dynamics365 48,582
https://learn.microsoft.com/education 22,924
ENTER key pressed: cancelling downloads.
Application ending.
コード例全体
次のコードは、この例の Program.cs ファイルの完全なテキストです。
using System.Diagnostics;
class Program
{
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
static readonly IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dynamics365",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://learn.microsoft.com/system-center",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/maui"
};
static async Task Main()
{
Console.WriteLine("Application started.");
Console.WriteLine("Press the ENTER key to cancel...\n");
Task cancelTask = Task.Run(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Console.WriteLine("Press the ENTER key to cancel...");
}
Console.WriteLine("\nENTER key pressed: cancelling downloads.\n");
s_cts.Cancel();
});
Task sumPageSizesTask = SumPageSizesAsync();
Task finishedTask = await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });
if (finishedTask == cancelTask)
{
// wait for the cancellation to take place:
try
{
await sumPageSizesTask;
Console.WriteLine("Download task completed before cancel request was processed.");
}
catch (OperationCanceledException)
{
Console.WriteLine("Download task has been cancelled.");
}
}
Console.WriteLine("Application ending.");
}
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
}
関連項目
次の手順
.NET