質問: allArg.Substring(0, 1); Thread.Sleep(800); return "OK"; })); // })).ConfigureAwait(false); return res; } } }コード ```
回答: こんにちは。 まずは結論から。 ConfigureAwaitを付けることで非同期メソッドの動作そのものは変わりません。そして、デッドロック問題を回避するため、**ほぼ全てのawait箇所でConfigureAwaitを書くのが基本である**と認識してください。 ConfigureAwaitの効果を説明する前に、awaitとTaskについて説明します。 awaitは「指定したTaskの処理が完了したときにメソッドの続きを実行する」キーワードになります。 実は非同期メソッドは、見た目は普通のメソッドのようですが、中でawaitを書く度にその場所で処理の前後が分割され、awaitしたTaskが完了した時に続きをスケジュールして実行する、いわゆる「処理の連結リスト」のようなものになっています。 そして、数珠つなぎで構成された非同期処理は、それぞれの分割処理の続きを実行する度に、任意の空いているコンテキスト(スレッド)に処理を放り込みます。「処理が書いた順番通りに実行されるなら、どのスレッドがどの処理を実行しても問題はない」という思想に基づいて設計されたものがTaskクラスなのです。 しかし、非同期メソッド中でawaitする前と後でコンテキストが異なると都合が悪い存在があります。それは、「UI処理等、特定のスレッドに依存する処理」です。 UI処理は、元々単一のスレッドでしか実行できないというフレームワークの設計による制約があります。 よって、最も非同期処理を必要とするシーンでTaskクラスとasync/awaitが使えないという残念な状況になってしまうことになります。 これを解決するために、awaitキーワードは、 * Taskを呼び出す前に、そのコンテキストをいったん保存する * Taskが完了した後の続きを実行するとき、そのコンテキストを復元して、そこで続きを実行する という設計を採用しました。 つまり、非同期メソッド上に実際に記述した処理は「全て単一のスレッド上で実行される」設計になったということです。 この設計によって、UI処理を非同期メソッドで記述出来るようになった代わりに、コンテキストの衝突によるデッドロックの問題を生み出してしまいました。 デッドロックは致命的な存在なので、さらにこれを回避するために「コンテキストの保存を無効化する」手段が必要になりました。それが、ConfigureAwaitメソッドなのです。 つまり、本来awaitはConfigureAwait(false)が書かれた状態がデフォルトであると考えることができます。 そして、UI処理等の、非同期メソッドの処理を一つのコンテキスト上で走らせたい時だけ、ConfigureAwaitを外せばいいのです。 質問のコードを例にすると、 * LedSwitchTaskメソッド中に、UIを操作するメソッドを書いていない * LedSwitchTaskメソッド中でawaitしている全ての非同期メソッドやTaskがUIにアクセスしていない という2点を満たす場合、LedSwitchTaskメソッド中の全てのawaitにConfigureAwaitを付けることができます。そして、付けなければなりません。 実際のところ、**WaitメソッドやResultプロパティを一切使用しなければ、デッドロックは発生することはなく、ConfigureAwaitを書かなくても障害につながることはない**のですが、ライブラリや汎用的な非同期APIを作成する際にはコンテキストの管理は絶対に必要になる知識とテクニックなので、ここできっちり覚えてしまった方がいいです。 Taskクラスを説明するのは難しく、理解できない文章になっていたらすみません。 また、以前の回答でも参考に記載しましたが、async/awaitについての解説記事の内容も参考にしてみてください。 質問の内容が過去の質問からの地続きになっていて、文脈が読み取りにくい質問になってきています。 質問単体からその背景が読み取れない質問はあまり良くないので、もしここでわからないところや疑問点があったら、コメント欄に質問してください。