IRONSOFTWAREHOME

C# AsyncとAwaitをマスターする

C# の Async と Await をマスターする

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}";
}

このコードは次の手順を実行します。

1.ストップウォッチを開始する:実行時間を測定します。 2.ウェブサイトリストを取得する:ウェブサイトのURLリストを取得します。 3.各ウェブサイトをダウンロードする:各ウェブサイトのコンテンツをダウンロードします。 4.レポートウェブサイト情報:ダウンロードしたコンテンツのURLと長さを表示します。 5.ストップウォッチを止める: タイマーを止め、合計実行時間を報告します。

同期操作の観察

Timがアプリケーションを実行して、同期動作のデモを行います。 彼は、ウェブサイトのダウンロード中にUIが反応しなくなり、ダウンロード完了後に結果が一度に表示されると指摘している。

同期操作

フォームが動かないという実用的なデモについては、9:20からのビデオを見て理解を深めてください。

非同期タスクの作成

ティムは、同期メソッドを非同期メソッドに変換することで、最初の問題に取り組んでいます。 これは、awaitというキーワードを使用することを含みます。 以下は、プロセスのステップバイステップの説明です:

1.既存の同期メソッドをコピーする

  • Timは、既存の同期メソッドをコピーし、非同期であることを示すように名前を変更します。
private async Task RunDownloadAsync()
{
    // Same code as RunDownloadSync, but will be modified for async
}
C#

2.非同期実行のためのダウンロード呼び出しの修正

  • Timはダウンロード呼び出しをTask.Runで包んで非同期的に実行します。
private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
    return await Task.Run(() => DownloadWebsite(websiteURL));
}
C#
  • 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);
    }
}
C#

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}";
}
C#
  • イベントハンドラは、非同期メソッドを呼び出しても、voidを返すことができることに注意してください。

UIの応答性への対応

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}";
}

正しいタイミングの確保

総実行時間が正しく報告されることを保証するために、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}";
}

非同期メソッドの完了を待つことで、実行時間が正確に測定され、取得された結果が表示されます。

並列非同期を作成する

Timは、並列実行を使用することで、各タスクが順次完了するのを待つという制限に対処します。 そのために彼がどのようにコードを修正したかを紹介しよう:

1.既存の非同期メソッドをコピーする

  • Tim は、既存の async メソッドを複製し、並列実行を示す名前に変更します。
private async Task RunDownloadParallelAsync()
{
    // Parallel execution logic will be added here
}
C#

2.タスクのリストを作成する

  • タスクのリストは、すべてのダウンロードタスクを保存するために作成されます。
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();

3.待たずにすべてのタスクを開始する

  • 各ダウンロードタスクをすぐに待つのではなく、ティムはタスクリストに追加します。
foreach (var website in websites)
{
    tasks.Add(DownloadWebsiteAsync(website));
}
C#

4.すべてのタスクが完了するのを待ちます

  • Task.WhenAllメソッドは、すべてのタスクの完了を待機するために使用されます。 このメソッドは、すべてのタスクが完了すると、結果の配列を返します。
WebsiteDataModel[] results = await Task.WhenAll(tasks);

5.結果を処理する

  • すべてのタスクが完了すると、Timはループで結果を処理します。
foreach (var result in results)
{
    ReportWebsiteInfo(result);
}
C#

以下は、並列実行メソッドの完全なコードです:

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

イベント ハンドラの更新

Timは、新しい並列実行メソッドを呼び出すために、ボタンをクリックするイベントハンドラを更新します。

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}";
}

RunDownloadParallelAsyncを待つことで、総実行時間が正確に測定され報告されます。

パフォーマンスの向上を観察する

Timは、並列実行でアプリケーションを実行することで、パフォーマンスの向上を実証しています。 その結果、逐次実行に比べて総実行時間が大幅に短縮された。

// Parallel execution
async Task RunDownloadParallelAsync();

ウェブサイトが同時にダウンロードされ、合計待ち時間が最も遅い個々のダウンロードの時間に短縮されるため、スピードの向上は明らかです。

Task.Run()でのメソッドラッピングと非同期メソッド呼び出し

Timは、元のメソッドを修正できない場合に、Task.Run()を使用して同期メソッドを包み、非同期化する概念を説明します。 ただし、メソッド自体をコントロールできるのであれば、非同期になるように修正するという望ましいアプローチも示している。

  1. Task.Run()でのメソッドラッピング:

    • このアプローチは、元のメソッドのコードを変更できないが、非同期で実行したい場合に便利です。
    private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
    {
        return await Task.Run(() => DownloadWebsite(websiteURL));
    }
    C#

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 };
}
C#

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

キーポイントの概要

Timは、asyncとawaitを使用するためのいくつかの重要なポイントについて締めくくります:

  • タスクを返すメソッドの保証: メソッドにTask<t>を返すべきです。

  • 信頼性のある操作のためのawaitの使用: 非同期操作の結果が必要な時にawaitキーワードを使用します。

  • 変更できないコードのためのタスクラッピング: 元のメソッドを変更できない場合に同期メソッドを包むためにTask.Run()を使用します。

  • 非同期メソッドを適切にマークする: 非同期操作であることを示すために必ずメソッド名にAsyncを付けてください。

  • 並列実行と逐次実行:タスク間の依存関係に基づいて、タスクを並列に実行できるか、お互いを待つべきかを決定します。

Timは、C#での非同期プログラミングがTaskによって簡単になったと強調します。 スレッドコンテキストやアパートメントモデルなどの複雑な部分は、舞台裏で管理されます。

結論

Tim CoreyのC#におけるasyncとawaitのチュートリアルは、非同期プログラミングをマスターするための貴重なリソースを提供します。 タスクの並列処理、UIの応答性、適切なコンテキストでasyncとawaitを使用することの重要性などの概念を注意深く説明することで、Timは、より高速で応答性の高いアプリケーションを作成するためのツールを開発者に提供します。 彼の詳細なウォークスルーは、メインスレッドをブロックすることなく長時間実行するタスクを効率的に処理する方法や、非同期操作や並列実行を管理するための戦略を示しています。

より深い理解と追加の実用的な例については、ティム・コーリーのビデオを見ることを強くお勧めします。 非同期 void メソッド、エラー処理、同期コンテキストの管理に関する洞察は、プロジェクトでこれらの概念を実装する能力をさらに高めるでしょう。

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!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

無料の30日間トライアルキーをすぐに入手してください。

Your trial license will be sent to your email address

制限なし。100% ロック解除済み。クレジットカード不要。

OR
bullet_checkedクレジットカードやアカウントの作成は不要です。制限なし。100% ロック解除済み。クレジットカード不要。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
義務のない相談を受ける
下記のフォームを記入するか、sales@ironsoftware.comにメールしてください。
あなたの詳細は常に守秘されます。
世界中の数百万人のエンジニアから信頼されています。
ライセンスはより安く
あなたの無料30日間の試用キーをすぐに入手。
クレジットカードやアカウントの作成は不要です。