Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
38 分 57 秒
非同期プログラミングは、その複雑な用語で難しく思われるかもしれませんが、アプリケーションの高速化と応答性の向上に大きなメリットをもたらします。Tim Corey 氏のビデオ"C# Async / Await - Make your app more responsive and faster with asynchronous programming"では、これらの機能を効果的に使用するための実践的なガイドを提供しています。
この記事では、Tim Corey氏の詳細なビデオを使用して、これらの概念を分解し、明確な例を通してasyncとawaitの実用的なアプリケーションを示します。 非同期イベント・ハンドラから複数タスクの処理、同期コンテキスト、エラー処理、非同期voidメソッドの使用などのよくある落とし穴まで、さまざまなシナリオを取り上げます。 この記事を読み終わる頃には、非同期プログラミングと、それを自分のC#アプリケーションに実装する方法について、しっかりと理解していることでしょう。
C#の非同期プログラミングは、アプリケーションのパフォーマンスを向上させるために不可欠であり、特にI/OバウンドやCPUバウンドの操作を行う場合に重要です。 awaitというキーワードにより、開発者は非同期コードを書くことができ、メインスレッドをブロックすることなく長時間実行されるタスクを実行できます。 メソッドにTask<t>を返すことを示します。 非同期メソッド内でawaitキーワードを使用することで、待機されるタスクが完了するまで実行を一時中断し、他の操作を同時に実行できるようにします。
タスクが順次実行され、呼び出し元のスレッドをブロックする可能性がある同期プログラミングとは異なり、非同期メソッドを使用すると、UIスレッドをフリーズさせることなく、バックグラウンド処理やユーザー入力の処理など、複数のタスクを同時に処理できます。 この記事では、awaitがどのように協力して動作するかを探求し、非同期voidメソッド、エラー処理、コンテキストスイッチ、同期コンテキストのような概念をカバーします。 複数の非同期処理を効率的に管理し、例外を処理して、呼び出しスレッド、UIスレッド、タスクの完了にわたってスムーズな実行を保証する方法を学びます。 このアプローチは、ファイルシステム、ネットワークリクエスト、同時リクエストなど、応答性が重要なシナリオに特に有効です。
Timは、同期操作と非同期操作の違いを説明することで、非同期プログラミングを紹介します。 同期プログラミングでは、タスクは順次実行されるため、タスクに時間がかかると遅延が発生します。非同期プログラミングでは、タスクを並列またはバックグラウンドで実行できるため、パフォーマンスと応答性が向上します。
ティムは、非同期プログラミングの2つの主な利点を強調します:
1.ユーザー インターフェイス (UI) の応答性の向上: タスクを非同期で実行することで、長時間実行される操作でも UI は応答性を維持します。
2.並列実行:独立したタスクを並列に実行できるため、完了までに必要な合計時間を短縮できます。
Timは、同期操作と非同期操作の違いを示すために、デモのWindows Presentation Foundation(WPF)アプリケーションをセットアップします。 アプリケーションには、同期的にタスクを実行するボタンと、非同期的にタスクを実行するボタンの2つがあります。

このアプリケーションには、出力を表示する結果ペインも含まれています。
Timは、同期操作の背後にあるコードを説明します。 Stopwatchは、タスクの実行時間を測定するために使用されます。
private void ExecuteSync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
List<string> websites = GetWebsiteList();
foreach (var website in websites)
{
string result = DownloadWebsite(website);
ReportWebsiteInfo(website, result);
}
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}
private List<string> GetWebsiteList()
{
return new List<string>
{
"https://www.yahoo.com",
"https://www.google.com",
"https://www.microsoft.com",
"https://www.cnn.com",
"https://www.codeproject.com",
"https://www.stackoverflow.com"
};
}
private string DownloadWebsite(string websiteURL)
{
WebClient client = new WebClient();
return client.DownloadString(websiteURL);
}
private void ReportWebsiteInfo(string website, string data)
{
ResultsWindow.Text += $"{website}: {data.Length} characters{Environment.NewLine}";
}Private Sub ExecuteSync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Dim websites As List(Of String) = GetWebsiteList()
For Each website In websites
Dim result As String = DownloadWebsite(website)
ReportWebsiteInfo(website, result)
Next
stopwatch.Stop()
ResultsWindow.Text &= $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End Sub
Private Function GetWebsiteList() As List(Of String)
Return New List(Of String) From {
"https://www.yahoo.com",
"https://www.google.com",
"https://www.microsoft.com",
"https://www.cnn.com",
"https://www.codeproject.com",
"https://www.stackoverflow.com"
}
End Function
Private Function DownloadWebsite(websiteURL As String) As String
Dim client As New WebClient()
Return client.DownloadString(websiteURL)
End Function
Private Sub ReportWebsiteInfo(website As String, data As String)
ResultsWindow.Text &= $"{website}: {data.Length} characters{Environment.NewLine}"
End Subこのコードは次の手順を実行します。
1.ストップウォッチを開始する:実行時間を測定します。 2.ウェブサイトリストを取得する:ウェブサイトのURLリストを取得します。 3.各ウェブサイトをダウンロードする:各ウェブサイトのコンテンツをダウンロードします。 4.レポートウェブサイト情報:ダウンロードしたコンテンツのURLと長さを表示します。 5.ストップウォッチを止める: タイマーを止め、合計実行時間を報告します。
Timがアプリケーションを実行して、同期動作のデモを行います。 彼は、ウェブサイトのダウンロード中にUIが反応しなくなり、ダウンロード完了後に結果が一度に表示されると指摘している。

フォームが動かないという実用的なデモについては、9:20からのビデオを見て理解を深めてください。
ティムは、同期メソッドを非同期メソッドに変換することで、最初の問題に取り組んでいます。 これは、awaitというキーワードを使用することを含みます。 以下は、プロセスのステップバイステップの説明です:
1.既存の同期メソッドをコピーする:
private async Task RunDownloadAsync()
{
// Same code as RunDownloadSync, but will be modified for async
}
2.非同期実行のためのダウンロード呼び出しの修正:
Task.Runで包んで非同期的に実行します。private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
return await Task.Run(() => DownloadWebsite(websiteURL));
}
awaitキーワードは、非同期タスクが完了するまで待機することをメソッドに保証します。3.メソッドが非同期であることを確認してください:
asyncキーワードを含むように更新され、Taskを返します。private async Task RunDownloadAsync()
{
List<string> websites = GetWebsiteList();
foreach (var website in websites)
{
var result = await DownloadWebsiteAsync(website);
ReportWebsiteInfo(website, result);
}
}
4.イベントを正しく処理する:
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}
Timは、awaitキーワードを使用することでUIが応答可能なままであることを示します。これにより、非同期タスクが実行中でもユーザーはウィンドウと対話できます。
// UI remains responsive
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}' UI remains responsive
Private Async Sub ExecuteAsync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Await RunDownloadAsync()
stopwatch.Stop()
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End Sub総実行時間が正しく報告されることを保証するために、Timはボタンクリックイベントハンドラーの呼び出しにawaitキーワードを追加します。
// Correctly waits for the asynchronous task to complete
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}' Correctly waits for the asynchronous task to complete
Private Async Sub ExecuteAsync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Await RunDownloadAsync()
stopwatch.Stop()
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End Sub非同期メソッドの完了を待つことで、実行時間が正確に測定され、取得された結果が表示されます。
Timは、並列実行を使用することで、各タスクが順次完了するのを待つという制限に対処します。 そのために彼がどのようにコードを修正したかを紹介しよう:
1.既存の非同期メソッドをコピーする:
private async Task RunDownloadParallelAsync()
{
// Parallel execution logic will be added here
}
2.タスクのリストを作成する:
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();Dim tasks As New List(Of Task(Of WebsiteDataModel))()3.待たずにすべてのタスクを開始する:
foreach (var website in websites)
{
tasks.Add(DownloadWebsiteAsync(website));
}
4.すべてのタスクが完了するのを待ちます:
Task.WhenAllメソッドは、すべてのタスクの完了を待機するために使用されます。 このメソッドは、すべてのタスクが完了すると、結果の配列を返します。WebsiteDataModel[] results = await Task.WhenAll(tasks);Dim results As WebsiteDataModel() = Await Task.WhenAll(tasks)5.結果を処理する:
foreach (var result in results)
{
ReportWebsiteInfo(result);
}
以下は、並列実行メソッドの完全なコードです:
private async Task RunDownloadParallelAsync()
{
List<string> websites = GetWebsiteList();
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();
foreach (var website in websites)
{
tasks.Add(DownloadWebsiteAsync(website));
}
WebsiteDataModel[] results = await Task.WhenAll(tasks);
foreach (var result in results)
{
ReportWebsiteInfo(result);
}
}Private Async Function RunDownloadParallelAsync() As Task
Dim websites As List(Of String) = GetWebsiteList()
Dim tasks As New List(Of Task(Of WebsiteDataModel))()
For Each website As String In websites
tasks.Add(DownloadWebsiteAsync(website))
Next
Dim results As WebsiteDataModel() = Await Task.WhenAll(tasks)
For Each result As WebsiteDataModel In results
ReportWebsiteInfo(result)
Next
End FunctionTimは、新しい並列実行メソッドを呼び出すために、ボタンをクリックするイベントハンドラを更新します。
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadParallelAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}Private Async Sub ExecuteAsync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Await RunDownloadParallelAsync()
stopwatch.Stop()
ResultsWindow.Text &= $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End SubRunDownloadParallelAsyncを待つことで、総実行時間が正確に測定され報告されます。
Timは、並列実行でアプリケーションを実行することで、パフォーマンスの向上を実証しています。 その結果、逐次実行に比べて総実行時間が大幅に短縮された。
// Parallel execution
async Task RunDownloadParallelAsync();' Parallel execution
Async Function RunDownloadParallelAsync() As Task
End Functionウェブサイトが同時にダウンロードされ、合計待ち時間が最も遅い個々のダウンロードの時間に短縮されるため、スピードの向上は明らかです。
Task.Run()でのメソッドラッピングと非同期メソッド呼び出しTimは、元のメソッドを修正できない場合に、Task.Run()を使用して同期メソッドを包み、非同期化する概念を説明します。 ただし、メソッド自体をコントロールできるのであれば、非同期になるように修正するという望ましいアプローチも示している。
Task.Run()でのメソッドラッピング:
private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
return await Task.Run(() => DownloadWebsite(websiteURL));
}
2.メソッドを非同期にする:
メソッドを修正できる場合は、適切な非同期APIを使用してメソッド自体を非同期に変換する方が良いでしょう。
TimはDownloadStringTaskAsyncを使用することでこれを示します。
private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
WebClient client = new WebClient();
string data = await client.DownloadStringTaskAsync(websiteURL);
return new WebsiteDataModel { URL = websiteURL, Data = data };
}
3.呼び出しメソッドの調整:
Task.Run()ラッパーを削除し、直接非同期メソッドを呼び出す必要があります。private async Task RunDownloadParallelAsync()
{
List<string> websites = GetWebsiteList();
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();
foreach (var website in websites)
{
tasks.Add(DownloadWebsiteAsync(website));
}
WebsiteDataModel[] results = await Task.WhenAll(tasks);
foreach (var result in results)
{
ReportWebsiteInfo(result);
}
}Private Async Function RunDownloadParallelAsync() As Task
Dim websites As List(Of String) = GetWebsiteList()
Dim tasks As New List(Of Task(Of WebsiteDataModel))()
For Each website As String In websites
tasks.Add(DownloadWebsiteAsync(website))
Next
Dim results As WebsiteDataModel() = Await Task.WhenAll(tasks)
For Each result As WebsiteDataModel In results
ReportWebsiteInfo(result)
Next
End FunctionTimは、asyncとawaitを使用するためのいくつかの重要なポイントについて締めくくります:
タスクを返すメソッドの保証: メソッドにTask<t>を返すべきです。
信頼性のある操作のためのawaitの使用: 非同期操作の結果が必要な時にawaitキーワードを使用します。
変更できないコードのためのタスクラッピング: 元のメソッドを変更できない場合に同期メソッドを包むためにTask.Run()を使用します。
非同期メソッドを適切にマークする: 非同期操作であることを示すために必ずメソッド名にAsyncを付けてください。
並列実行と逐次実行:タスク間の依存関係に基づいて、タスクを並列に実行できるか、お互いを待つべきかを決定します。
Timは、C#での非同期プログラミングがTaskによって簡単になったと強調します。 スレッドコンテキストやアパートメントモデルなどの複雑な部分は、舞台裏で管理されます。
Tim CoreyのC#におけるasyncとawaitのチュートリアルは、非同期プログラミングをマスターするための貴重なリソースを提供します。 タスクの並列処理、UIの応答性、適切なコンテキストでasyncとawaitを使用することの重要性などの概念を注意深く説明することで、Timは、より高速で応答性の高いアプリケーションを作成するためのツールを開発者に提供します。 彼の詳細なウォークスルーは、メインスレッドをブロックすることなく長時間実行するタスクを効率的に処理する方法や、非同期操作や並列実行を管理するための戦略を示しています。
より深い理解と追加の実用的な例については、ティム・コーリーのビデオを見ることを強くお勧めします。 非同期 void メソッド、エラー処理、同期コンテキストの管理に関する洞察は、プロジェクトでこれらの概念を実装する能力をさらに高めるでしょう。
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy