非同期コンソール アプリケーションが完了するのを待たなければ、非同期コンソール アプリケーションを取り消すことができます。 このトピックの例に従うことで、Web サイトの一覧の内容をダウンロードするアプリケーションにキャンセルを追加できます。 CancellationTokenSource インスタンスを各タスクに関連付けることで、多くのタスクを取り消すことができます。 Enter キーを選択した場合は、まだ完了していないすべてのタスクを取り消します。
このチュートリアルの内容:
- .NET コンソール アプリケーションの作成
- キャンセルをサポートする非同期アプリケーションの記述
- キャンセル通知のデモンストレーション
前提条件
- 最新の .NET SDK
- Visual Studio Codeエディター
- C# DevKit
サンプル アプリケーションを作成する
新しい .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
メソッドは 非同期メインと見なされるようになりました。これにより、実行可能ファイルへの非同期エントリ ポイントが可能になります。 コンソールにいくつかの指示メッセージを書き込み、cancelTask
という名前のTask インスタンスを宣言します。これにより、コンソール のキー ストロークが読み取られます。 Enter キーを押すと、CancellationTokenSource.Cancel()の呼び出しが行われます。 これにより、キャンセルが通知されます。 次に、SumPageSizesAsync
メソッドからsumPageSizesTask
変数が割り当てられます。 その後、両方のタスクが 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