async/await
await XxxxAsync();
と記述した時、XxxxAsync() が終了するまで制御をOSに返し、
それ以降の実行を待機する仕組み
async 関数を await で実行
void を返す ... fire & forget
Task<int> を返す ... 非同期処理完了後に同期して int を返す
Task を返す ... 非同期処理完了後に同期して void を返す
fire & forget とは
await XxxxAsync(); と、その下に続くコードが並列実行される
(ノンブロッキング関数的なイメージ)
await
await XxxxAsync(); の下に続くコードは、
XxxxAsync() から戻ってきた後に実行されるコールバックとしてコンパイルされる。
コールバックが実行されるスレッドを決定するのが同期コンテキスト。
同期コンテキスト(synchronization context)
Task オブジェクトがタスク実行後に戻る文脈を保持する。
await 呼び出し側の SynchronizationContext.Current に戻ってくるように設定される。
デフォルトでは task.ConfigureAwait( true ) となっており、同期コンテキストが適用される。
実行環境による同期コンテキストの違い
WPF等GUI … UIスレッド
ASP.NET … リクエストとレスポンスが同一スレッド
コンソール … null(同期コンテキストが存在しない)
※同期コンテキストが存在しない
= 戻るべきスレッドを規定しない
= スレッドプールの任意のスレッドに戻る(というか移行)
同期コンテキストを無視したいとき
await XxxAsync().ConfigureAwait( false );
コンソールと同じ動作となる
単に、何かの処理を別スレッドで実行したい場合について
await/async で、せっかくコールバックをインデントのネストから解放できたのに、
任意の処理を別スレッドでさせたいときなんかに Task.Run() で処理を書かざるを得ないのはダサく感じる。
平坦に書きたいです。
平坦に書きたいです。
同期コンテキストがなければ await Task.Yield() でそれ以降を別スレッドに移せるぽいが、
そうでない場合は await Task.Run( () => {} ).ConfigureAwait( false ); などと書かないとダメかも。
でも無意味なタスクオブジェクトが発生したりしそう。
そもそも、async/await は非同期タスクを待ち受けるための構文であって、
スレッド実行を平坦に記述するという汎用的な目的で使うべきものではない、ということか。
同期させるスレッドを自由に切り替えできればいいのにと思うけど、そういう思想のものじゃない、のか…。
そもそも、async/await は非同期タスクを待ち受けるための構文であって、
スレッド実行を平坦に記述するという汎用的な目的で使うべきものではない、ということか。
同期させるスレッドを自由に切り替えできればいいのにと思うけど、そういう思想のものじゃない、のか…。