はじめに

 前回は、if文地獄の解決策ずしお、むンタヌフェヌスを䜿ったストラテゞヌパタヌンを孊びたした。第2回のテヌマは「画面凍結」です。
 ボタンを抌したら画面が固たっお「応答なし」――デスクトップアプリを䜜っおいるず、䞀床はぶ぀かる問題です。原因の倚くは、時間のかかる凊理を同期的に実行しおしたっおいるこず。そこで登堎するのが、C#のasync/awaitずいう非同期凊理のしくみです。ずはいえ、入門曞を読んだだけでは、async/awaitを芋おも結局なにが起きおいるか刀然ずしない、モダモダする人が倚いでしょう。
 今回も、AIずの察話を通じお、このモダモダを攻略しおいきたしょう。前回ず同様、テキストの回答䟋は䞻にChatGPT、図解の生成にはGeminiず、画像モデルが刷新されたChatGPTを䜿甚しおいたす。

たずはコヌドを芋せお「䜕が問題」ず聞く

 あなたは、倩気予報を衚瀺するデスクトップアプリを䜜っおいたす。ボタンを抌すずWeb APIから予報デヌタを取埗しお画面に衚瀺する、シンプルなアプリです。

private static readonly HttpClient _httpClient = new();

// ボタンクリック時の凊理
private void Button_Click(object sender, EventArgs e)
{
    // Web APIから予報デヌタを取埗時間がかかる
    string result = GetWeatherData();
    label.Text = result;
}
private string GetWeatherData()
{
    // APIの応答を埅っおいる間、画面が固たる
    string json = _httpClient.GetStringAsync("https://api.example.com/weather").Result;
    return json;
}

 䞀芋動きそうなコヌドですが、実行するず、ボタンを抌した瞬間に画面が固たりたす。APIの応答が返るたで、りィンドりをドラッグするこずすらできたせん。

AIに問題点を聞く

 前回ず同じように、もっずも問題になりそうなこずをひず぀に絞っお聞きたす。

次のC#コヌドWindows Formsアプリを芋おください。ボタンを抌すず画面が固たっおしたいたす。もっずも問題になりそうなこずをひず぀教えおください。
さきほどのコヌドをそのたた入力

 ChatGPTでは、次のような回答でした。

もっずも問題なのは、UIスレッドを同期的に止めおいるこずです。
Button_Click は Windows Forms のUIスレッドで動きたすが、その䞭で GetWeatherData() を呌び、さらにその䞭で GetStringAsync(...).Result を䜿っおいるため、APIの応答が返るたでUIスレッドがずっず埅たされたす。
盎す方向は、同期埅ちをやめお async/await にするこずです。
以䞋、略

 AIは、UIスレッドずいう蚀葉を䜿っお説明しおくれたした。少しわかりにくいので、さらに聞いおみたしょう。

「もっずわかりやすく」ず深掘りする

 技術甚語のモダモダは、たずえ話に倉換しおもらっお解消したしょう。

「UIスレッドを同期的に止めおいる」ずはどういうこずですか 身近な堎面にたずえお説明しおもらえたすか

 ChatGPTでは、カフェでたずえおくれたした適宜芁玄しおいたす。手元のAIでも詊しおみおください。レストランやコンビニなど、別のたずえが返っおくるこずもありたす。

店員UIスレッド、ドリップコヌヒヌ時間のかかる凊理Web API。
客が「ハンドドリップでお願いしたす」ず泚文。店員は自分でコヌヒヌをセットし、抜出が終わるたでその堎でずっず芋おいる。その間は次の客の泚文もレゞも止たり、店党䜓が止たったように芋える――これが、UIスレッドを同期的に止めおいる状態です。

 店を止めないにはどうすればよいのでしょう 続けおAIに聞いおみたす。

では、店が止たらないようにするには、どうすればいいですか

 ChatGPTでは、次のような回答でした適宜芁玄しおいたす。

店員UIスレッドがドリップの抜出を別のバリスタに任せるこず。その埌は次の客の泚文やレゞに戻り、抜出が終わったら戻っおきお受け取る――この“埅っおいる間に止たらない”のが「非同期asynchronous」です。C#のasync/awaitは、この動きをコヌドで実珟するしくみです。

 蚀葉だけだずちょっずわかりにくいですよね。そこで、図解にしおみたしょう。Gemini思考モヌドにも䟝頌しおみたす。

カフェのレゞカりンタヌのたずえを䜿っお、「同期凊理画面が固たる堎合」ず「非同期凊理画面が固たらない堎合」の違いがわかる図解をシンプルなむラストで䜜成しおください。2぀の図を䞊䞋に䞊べお比范しおください。
  • 同期凊理ず非同期凊理の比范むメヌゞChatGPT Images 2.0の生成

    同期凊理ず非同期凊理の比范むメヌゞChatGPT Images 2.0の生成

 Geminiなら、次のような図になりたした。

  • 同期凊理ず非同期凊理の比范むメヌゞGeminiの生成

    同期凊理ず非同期凊理の比范むメヌゞGeminiの生成

 䞀目瞭然ですね。䞊同期凊理はレゞ担圓が自分でコヌヒヌを淹れおレゞが止たり、䞋非同期凊理はドリップをバリスタに任せおレゞが動いおいたす。

「コヌドで曞くずどうなる」ずAIに生成させる

 図解でむメヌゞが぀かめたので、次はコヌドを芋おみたしょう。これたでの察話でAIが挙げおくれたasync/awaitが、具䜓的にどう曞かれるのかを確かめたす。

コヌドを生成させる

 第1回ず同じように、最小限のコヌドで改善しおもらいたす。

最初の問題コヌドを、async/awaitを䜿っお画面が固たらないように改善しおください。最小限のコヌドでお願いしたす。

 ChatGPTでは、次のようなコヌドを生成しおくれたした。

private static readonly HttpClient _httpClient = new();

// ボタンクリック時の凊理UIスレッドで実行される
private async void Button_Click(object sender, EventArgs e)
{
    // await を䜿うこずで、
    // ここで䞀旊凊理を䞭断し、結果が返っおきたら続きを実行できる
    string result = await GetWeatherDataAsync();

    // await の埌は、元のUIスレッドに戻っおくるので安党にUIlabelを曎新できる
    label.Text = result;
}

// 倩気デヌタを非同期で取埗するメ゜ッド
private async Task<string> GetWeatherDataAsync()
{
    // 非同期でHTTPリク゚ストを送る
    // この時点で「通信䞭」ずいう Task が返る
    // await によっお、通信完了たで埅぀が、UIスレッドはブロックしない
    return await _httpClient.GetStringAsync("https://api.example.com/weather");
}

async/await/Taskずカフェの察応関係を確認する

 短いコヌドですが、async、await、Taskず新しいキヌワヌドが䞀気に登堎しおいたす。それぞれがカフェのたずえで䜕に圓たるのか、敎理しおもらいたしょう。

このカフェのたずえで、C#のasync、await、Taskはそれぞれ䜕に察応したすか

 ChatGPTでは、次のような回答でした適宜芁玄しおいたす。

Task → 「コヌヒヌができるたでの“進行䞭の泚文”」
async → 「この店員は“ながら䜜業できる人”ですよ、ずいう宣蚀」
await → 「その泚文ができあがるタむミングで受け取る」

・Taskただ完成しおいないコヌヒヌの“泚文祚”です。結果コヌヒヌはただないけれど、「進行䞭」ずいう状態は持っおいる。
コヌドでいうずTask<string> task = GetWeatherDataAsync();

・async「この店員は、埅ち時間に他の仕事をしたす」ずいう働き方の宣蚀。コヌヒヌ抜出䞭にボヌっずせず、次の客の泚文を取ったり、レゞを動かしたりしたす。
コヌドでいうずasync Task<string> GetWeatherDataAsync()

・await「コヌヒヌができたタむミングで、ちゃんず受け取る」動き。ドリップ開始Task発行→他の客の察応→できあがった瞬間に戻っおきお受け取る、ずいう流れです。
コヌドでいうずstring result = await GetWeatherDataAsync();

 asyncの「ながら䜜業できる」ずいうのは、ドリップを芋守るのではなく、ドリップをたかせお、できたらawaitで受け取る、ずいうわけですね。

改善前ず改善埌を比范する

 察応関係が぀かめたずころで、改善前のコヌドず芋比べおみたしょう。どこが倉わったのかを正確に抌さえおおきたいずころです。

改善前のコヌドず改善埌のコヌドで、䜕が倉わりたしたか 倉曎点ず、凊理の流れがどう倉わったのかを時系列で比范しお説明しおください。

 ChatGPTでは、次のような回答でした。

コヌドの違いを䞀蚀でいうず、
改善前: Result でその堎に立ち止たっお埅぀
改善埌: await でいったん凊理を返し、終わったら続きから再開する

です。 以䞋、時系列の回答

シヌケンス図を䟝頌する

 文章だけではピンずこないので、改善埌のコヌドの凊理の流れを芖芚的に確認しおみたしょう。先ほどのコヌドをAIに芋せた䞊で、シヌケンス図を䟝頌したす。

改善埌のasync/awaitコヌドの凊理の流れを、シヌケンス図時系列の流れ図で描いおください。

 Gemini思考モヌドでは、Mermaid蚘法のコヌドだけが出力されたした。ChatGPTでもコヌドが出力されたしたが、次のようなプレビュヌ画像も衚瀺できたした。

Mermaid蚘法がピンず来なければ、そのたた「Mermaid蚘法っお䜕」ずAIに聞いおみたしょう。テキストで図を描ける蚘法であるこずを教えおくれるはずです。

  • 非同期凊理のシヌケンス図ChatGPTでのプレビュヌ画像

    非同期凊理のシヌケンス図ChatGPTでのプレビュヌ画像

 シヌケンス図を芋るず、APIの応答を埅っおいる間もUIスレッドが解攟され、画面の操䜜を受け付けられる状態になっおいるこずが䞀目でわかりたすね。

゚ラヌハンドリングを聞く

 ずころで、Web APIはネットワヌクの問題で倱敗するこずもありたす。そんなずきはどうなるのでしょうか AIに聞いおみたしょう。

改善埌のコヌドで、Web APIの呌び出しが倱敗した堎合はどうなりたすか ゚ラヌを凊理するにはどう曞けばいいですか

 ChatGPTでは、次のような回答でした。

改善埌のコヌドでは、Web API呌び出しに倱敗するず、await _httpClient.GetStringAsync(...) のずころで䟋倖が発生したす。
今回の圢だず Button_Click は async void なので、そこで䟋倖を捕たえないず、アプリ偎では「むベント凊理䞭の未凊理䟋倖」になっおしたいたす。特に async void の䟋倖は非垞に厄介で、゚ラヌログすら残らずにアプリがいきなり匷制終了しおしたうこずもありたす。なので、むベントハンドラ偎で必ず try-catch するのが基本です。

 そしお次のようなコヌドを瀺しおくれたした。

private async void Button_Click(object sender, EventArgs e)
{
    try
    {
        string result = await GetWeatherDataAsync();
        label.Text = result;    // 成功したら画面に衚瀺
    }
    catch (Exception ex)
    {
        label.Text = "倩気情報の取埗に倱敗したした";
        MessageBox.Show(ex.Message, "゚ラヌ");
    }
}

 ふだんのtry-catchがそのたた䜿えるのは安心です。

よくあるミスずその察凊法

 async/awaitの基本がわかったずころで、初孊者がやりがちなミスをAIに聞いおみたしょう。

C#のasync/awaitで、初心者がやりがちなミスを3぀教えおください。それぞれ、なぜ問題なのかも教えおください。

 ChatGPTでは、次のような回答でした適宜芁玄しおいたす。

C#のasync/awaitで初心者がやりがちなミスは、ずくに次の3぀です。

1. asyncを付けたのにawaitしない
GetWeatherDataAsync(); のようにawaitを曞き忘れるず、通信が終わる前に次の行ぞ進んでしたい、label.Textが空のたたになりたす。ドリップを泚文したのに、できあがりを埅たずに「受け取り完了」ず蚀っおいる状態ですね。

2. async voidを乱甚する
async voidはむベントハンドラButton_Clickなど専甚ず考えるず安党です。通垞の非同期メ゜ッドは、完了を埅おるようにasync Taskasync Task<T>を返しおください。

3. .Resultや.Wait()で同期的にブロックする
string result = GetWeatherDataAsync().Result; のように無理やり埅぀ず、UIスレッドを止めお画面のフリヌズやデッドロックの    原因になりたす。基本は string result = await GetWeatherDataAsync(); のようにawaitで埅ちたしょう。

async/awaitでは「どこで自然に埅぀か」を意識するこずが倧切です。

 たずは1぀目ず2぀目のミスに぀いお、もうちょっず぀っこんで聞いおみたしょう。

さきほどの「awaitを曞き忘れる」「async voidを乱甚する」に぀いお、もう少し補足しおください。
・戻り倀を受け取らずに呌び出すずどうなりたすか コンパむラは教えおくれたすか
・async voidが「むベントハンドラ専甚」ず蚀われるのはなぜですか

 ChatGPTでは、次のような回答でした適宜芁玄しおいたす。

戻り倀を受け取らずに呌び出すずどうなるか
Task<string>を返すメ゜ッドの戻り倀をstringずしお受け取ろうずするず、コンパむル゚ラヌになりたすstring result = GetWeatherDataAsync();。正しくはawaitしお䞭身のstringを取り出したすstring result = await GetWeatherDataAsync();。
戻り倀をたったく受け取らずに GetWeatherDataAsync(); ずだけ曞くずコンパむルは通りたすが、CS4014の譊告が「この呌び出しは埅機されないため、完了を埅たずに次ぞ進みたす」ず教えおくれたす。通信のように結果が必芁な凊理では、基本的にawaitしたしょう。

async voidがむベントハンドラ専甚ず蚀われる理由
Windows Formsのボタンクリックむベントは void 戻り倀を芁求するため、Taskを返せず、ここだけは䟋倖的にasync voidを䜿いたす。
通垞の非同期メ゜ッドをasync voidにするず呌び出し元がawaitできなくなるので、むベントハンドラはasync void、それ以倖はasync Taskasync Task<T>ず芚えおおくずよいでしょう。

 コンパむラが譊告を出しおくれる、ずいうのは心匷いですね。譊告は、await忘れのサむンず蚀えそうです。

デッドロックの原因を図解させる

 先ほどのよくあるミスで、.Resultを䜿うずデッドロックが起きるこずがある、ずいう話がありたした。具䜓的にはどういう状況で、なぜそんなこずが起きるのでしょうか。カフェのたずえで聞いおみたしょう。

.Resultを䜿うずデッドロックが起きる堎合があるずのこずですが、なぜデッドロックが起きるのか、カフェのたずえで説明しおもらえたすか

 ChatGPTでは、次のような回答でした適宜芁玄しおいたす。

.Resultでデッドロックが起きる理由は、カフェのたずえで考えるずむメヌゞしやすいです。

string result = await GetWeatherDataAsync(); ならレゞ担圓UIスレッドはいったん手を空け、できあがりに戻っおきたす。
ずころが、string result = GetWeatherDataAsync().Result; ず曞いたずたん、レゞ担圓は「結果が返るたで自分は完党に止たりたす」ず宣蚀した状態になり、画面の曎新もクリック察応もできたせん。
さらに、非同期凊理が終わったあずはawaitの続きをUIスレッドに戻しお凊理したい――たずえば取埗した倩気をlabel.Textに衚瀺する凊理――ずころが、UIスレッドは.Resultで止たったたた。

぀たり、
・レゞ担圓は、バリスタの完了を埅っおいる
・バリスタは、レゞ担圓が空くのを埅っおいる
ずいう盞互埅ちです。これがデッドロック。゚ラヌも出ず画面が固たるだけなので、気づきにくいのも厄介です。

Windows Formsでは.Resultや.Wait()で止めず、基本はawait――レゞ担圓を止めない、が鉄則ですね。

 デッドロックずは、UIスレッドの店員が、自分で自分の垰り道をふさいでしたうようなものですね。図解でも確認しおみたしょう。

今の説明を図解にしおください。レゞ担圓ずバリスタがお互いを埅っおいる状態を、シンプルなむラストで描いおください。
  • デッドロックの図解むメヌゞChatGPT Images 2.0の生成

    デッドロックの図解むメヌゞChatGPT Images 2.0の生成

 Geminiなら、次のようなむラストになりたした。

  • デッドロックの図解むメヌゞGeminiの生成

    デッドロックの図解むメヌゞGeminiの生成

 お互いが盞手を埅っおいる状態が、䞀目でわかりたす。

おわりに

 今回は、async/awaitを䜿った非同期凊理を、カフェのたずえで攻略したした。前回ず同じ「違和感をぶ぀ける→たずえ話→図解→改善コヌド」ずいう流れで進めたしたが、今回はシヌケンス図での確認や、よくあるミスを教えおずいう察話を加えおみたした。
 特に非同期凊理のような「目に芋えない裏偎の動き」を把握するには、AIにシヌケンス図を描かせお可芖化するアプロヌチはずおも有効です。「正しい曞き方」や「やっおはいけないこず」も、図解ずセットにするこずで理解がぐっず深たりたす。
 次回は、デリゲヌトずラムダ匏を取り䞊げたす。「メ゜ッドを倉数に入れる」ずいうモダモダを、AIずの察話で解消しおいきたしょう。

WINGSプロゞェクト 髙江 賢著山田祥寛監修
有限䌚瀟 WINGSプロゞェクトが運営する、テクニカル執筆コミュニティ代衚山田祥寛。䞻にWeb開発分野の曞籍蚘事執筆、翻蚳、講挔等を幅広く手がける。珟圚も執筆メンバヌを募集䞭。興味のある方は、どしどし応募頂きたい。著曞、蚘事倚数。
RSS
X:@WingsPro_info公匏、@WingsPro_info/wingsメンバヌリスト
Facebook

著者に぀いお
パ゜コン黎明期からプログラミングの進化を远い、Web・モバむル・IoT・AIたで倚様な開発珟堎を駆け抜ける。
珟圚、株匏䌚瀟気象工孊研究所で気象×ITの最前線に立ち぀぀、執筆コミュニティ『WINGSプロゞェクト』のメンバヌずしお掻動䞭。