⏱️非同期プログラミング [C#]

async/await C#

C#におけるasync/awaitを使用した非同期プログラミングは、UIのフリーズ防止やI/O処理(ファイル、ネットワーク)の効率化に不可欠です。

最新の標準的な書き方と、押さえておくべきポイントを整理しました。


1. 基本的な構文

非同期メソッドを定義するには、メソッドに async 修飾子を付け、戻り値を Task または Task<T> にします。

C#
// 非同期メソッドの定義
public async Task<string> GetDataAsync(string url)
{
    using var client = new HttpClient();
    
    // 非同期処理を待機(await)
    // awaitしている間、スレッドは解放され、他の処理が可能になる
    string result = await client.GetStringAsync(url);
    
    return result;
}

重要なルール

  • 戻り値: 値を返さない場合は Task、値を返す場合は Task<T> を使用します。
  • 命名規則: メソッド名の末尾には Async を付けるのが一般的です。
  • await: 非同期処理の結果が必要な箇所で await を呼び出します。

2. 非同期処理の制御フロー

await キーワードに到達すると、メソッドの実行は一時停止し、制御が呼び出し元に戻ります。バックグラウンドでの処理が完了すると、続きから実行が再開されます。


3. 複数の非同期処理を並列で実行する

複数の処理を一つずつ待つのではなく、同時に開始してすべて終わるのを待つことでパフォーマンスが向上します。

C#
var task1 = DoWork1Async();
var task2 = DoWork2Async();

// 両方のタスクが完了するまで待機
await Task.WhenAll(task1, task2);

// 結果を受け取る場合
var results = await Task.WhenAll(GetVal1Async(), GetVal2Async());

4. キャンセル処理 (CancellationToken)

最新のC#では、途中で処理を中断できるように CancellationToken を渡すのがマナーです。

C#
public async Task DownloadFileAsync(string url, CancellationToken ct)
{
    using var client = new HttpClient();
    
    // キャンセルされた場合に例外を投げる
    var response = await client.GetAsync(url, ct);
    
    // または手動でチェック
    ct.ThrowIfCancellationRequested();
}

5. 推奨されるベストプラクティス

項目推奨される書き方避けるべき書き方
待機方法await task;task.Wait()task.Result (デッドロックの原因)
戻り値async Taskasync void (例外ハンドリングができなくなるため)
例外処理try-catch で await を囲む例外を無視する
最適化短時間で終わる場合は ValueTask全てに ValueTask を使う (複雑化を避ける)

補足:ValueTask について

高頻度で呼び出され、かつ多くの場合で即座に完了する(非同期にする必要がない場合がある)処理には、メモリ負荷を抑えるために ValueTask<T> を検討してください。


6. 例外ハンドリング

非同期メソッド内での例外は、await したタイミングでキャッチできます。

C#
try
{
    await SomeAsyncWork();
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"ネットワークエラー: {ex.Message}");
}

非同期処理は、歴史的に色々な書き方があり、なかなか理解するのが難しかったのですが、async/awaitにより、簡単に扱えるものになりました。実はこれコンパイル技術の賜物なのです。