本文へスキップ
BecomeCoder

C#実践コース · 第1章 非同期プログラミング · レッスン6

デッドロックと正しい作法

ブラウザで完結

導入

非同期には有名な落とし穴があります。awaitすべきところで.Result.Wait()を使うと、処理が永久に止まるデッドロックを招くことがあります。正しい作法を身につけましょう。

図解

flowchart TB
    G["非同期処理"] -->|await で待つ| OK["正しい:固まらず完了 ✅"]
    G -->|Result や Wait でブロック| NG["危険:状況次第で永久停止 🛑"]
    style OK fill:#e8f5e9
    style NG fill:#ffebee

サンプル

// ✅ 正しい: 非同期は最後まで await でつなぐ
int value = await GetValueAsync();
Console.WriteLine(value);   // 42

// ❌ 危険(このエンジンでも実際に停止/エラーになるため実行しない):
//    int bad = GetValueAsync().Result;   // .Result はブロッキング → デッドロックの温床
//    GetValueAsync().Wait();             // .Wait() も同様

async Task<int> GetValueAsync()
{
    await Task.Delay(30);
    return 42;
}
  • awaitすべき箇所で.Result / .Wait()を使うとブロッキングになり、デッドロックの原因になる
  • 原則:非同期は呼び出しの一番上までasync/awaitでつなぐ(“async all the way”)
  • async voidは例外を捕捉できず危険。イベントハンドラ以外ではasync Taskを使う

演習

// TODO: GetNameAsync を「正しい作法」で await し、"名前: Taro" と出力してください
//       (.Result や .Wait() は使わない)
async Task<string> GetNameAsync() { await Task.Delay(10); return "Taro"; }
___
  • 期待される出力: 名前: Taro
ヒント1を見る

string name = await GetNameAsync();

ヒント2を見る

Console.WriteLine($"名前: {await GetNameAsync()}");

まとめ

  • .Result / .Wait()のブロッキングはデッドロックの温床
  • 非同期は一番上までasync/awaitでつなぐ
  • async voidは避け、async Taskを使う

次回: 複数の処理が同じデータを触るときの「スレッド安全」です。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。