await task が確実にスレッドプールスレッドで実行させるようにするには?
2017/01/09追記
コメントで以下のご指摘を頂きました。ご注意下さい。※但し、私の実験した環境においては確実にスレッドプールスレッドで実行されています。
await Task.Delay(1).ConfigureAwait(false); と書いても、awaitの次の行がスイッチされたコンテキストで実行されるとは限りません
本体のTaskをawaitする前に、
await Task.Delay(1).ConfigureAwait(false);
を呼び出すだけ。
Delay(1)
は必ず(?)スレッドプールスレッドで実行され、そしてConfigureAwait(false)
することで、UIスレッドに戻ってこないようにする、という仕組み。
背景
await した task は必ずしもスレッドプールスレッドで実行されるとは限らない。
MSDNのどこかにもそんなことが明記されていたし、実際にトータルで数秒かかる処理(IO待ち1秒+その他)が全てUIスレッドで実行されるということも頻発した。(環境依存が強そう。Windows10で頻発。)
それによって数秒間画面が固まり、その間ボタンのEnabled/Disabledも画面に反映されず… というのは、要件によっては大問題となる。
そのため、必ずスレッドプールスレッドで実行してもらう(UIスレッドを自由にさせてあげる)方法が必要となった。
以上。
ライブラリでの非同期実装について
##ライブラリ内でTask.Runを使わない
スレッド、特にスレッドプールのスレッドはグローバルに共有されているリソースで、アプリケーション開発者に属しています。ライブラリの作者はTask.Runを使ったり、スレッドを作るメソッドを作成するべきではありません。どのようなタイミングでスレッドを追加するか決めるのはアプリケーション開発者の権利と責任です。
await するなら、ConfigureAwait(false) する
不用意にUIスレッドに戻らないよう。
await XxxAsync.ConfigureAwait(false)
参照
とても勉強になりました↓
.NET非同期処理(async-await)を制御する、様々な方法 – kekyoの丼
できる!C#で非同期処理(Taskとasync-await) – kekyoの丼
TAP (Task-based Asynchronous Pattern) 非同期メソッドのガイドライン
http://qiita.com/chocolamint/items/ed4999cccf011653cb78
HttpClient詳解、或いは非同期の落とし穴について
http://www.slideshare.net/neuecc/httpclient
.NETで非同期ライブラリを正しく実装する
http://www.infoq.com/jp/articles/Async-API-Design
async/await ~非同期なライブラリは楽じゃない~
http://qwerty2501.hatenablog.com/entry/2014/04/24/235849