非同期メソッド呼び出し後のスレッドについて
Metro スタイルアプリを作成する場合、時間がかかる処理は全て非同期メソッドを呼び出す事になります。
UI スレッドから、await を指定して非同期メソッドを呼び出した場合、非同期メソッド内の処理は別スレッドで処理され、UI スレッドをブロックしないようになっています。
そして、非同期メソッド後の処理は UI スレッドに戻って実行されるようになっていて、そのままコントロールにアクセスできます。
例として、以前 HTTP 通信の確認を行った時のソースを一部取り出してみます。
HTTP通信と検索機能 - rebuild
public async void SearchData(string keyword) { // 指定された検索キーワードを使ってリクエストURLを生成 var sb = new StringBuilder(); sb.Append("http://api.atnd.org/events/?count=30&format=json"); if (!String.IsNullOrEmpty(keyword)) { sb.AppendFormat("&keyword={0}", keyword); } // リクエストを送信して結果文字列を取得 var client = new HttpClient(); client.MaxResponseContentBufferSize = 1024 * 1024 * 10; var result = await client.GetStringAsync(sb.ToString()); // レスポンスを解析して一覧に追加 var rootElement = Windows.Data.Json.JsonObject.Parse(result); var eventElements = rootElement.GetNamedArray("events"); foreach (var elem in eventElements) { var title = elem.GetObject().GetNamedString("title"); var started_at = elem.GetObject().GetNamedString("started_at"); var place = elem.GetObject().GetNamedString("place"); var event_url = elem.GetObject().GetNamedString("event_url"); var item = new EventDataItem(title, started_at, place, event_url); _source.Items.Add(item); } }
このソースのメソッドを UI スレッドから呼び出した場合、GetStringAsync メソッドが非同期で実行され、その後の処理は UI スレッドで実行されます。
このため、最後の _source.Items.Add(item) の部分をそのまま実行しても動作します(この部分はコントロールにバインドされているデータソースへの追加になりますので、UI スレッド以外から呼び出すと例外が発生します)。
これで動作上は問題ありませんが、例としてあげたソースのように、通信処理後になんらかの処理を行ってからコントロールに設定するような事を考えると、通信後の処理で UI スレッドで実行する必要があるのはコントロールへの設定だけです。
それ以外の通信結果の解析部分については、UI スレッドで処理する必要はありませんし、UI スレッドで処理してしまうと、その間 UI をブロックしてしまうという問題もあります。
そのため、非同期処理後に行っている処理に時間がかかるような場合は、非同期処理後に UI スレッドに戻らないように指定をします。
非同期処理後に UI スレッドに戻らないようにするためには、Task クラスの ConfigureAwait メソッドに引数として false を指定して呼び出します。
Task クラスは非同期メソッドの戻り値として返されますので、非同期メソッドに続けて記述すれば呼び出せます。
例としてあげたソースを変更してみます。
public async void SearchDataEx(string keyword) { // 指定された検索キーワードを使ってリクエストURLを生成 var sb = new StringBuilder(); sb.Append("http://api.atnd.org/events/?count=30&format=json"); if (!String.IsNullOrEmpty(keyword)) { sb.AppendFormat("&keyword={0}", keyword); } // リクエストを送信して結果文字列を取得 var client = new HttpClient(); client.MaxResponseContentBufferSize = 1024 * 1024 * 10; var result = await client.GetStringAsync(sb.ToString()).ConfigureAwait(false); // レスポンスを解析して一覧に追加 var rootElement = Windows.Data.Json.JsonObject.Parse(result); var eventElements = rootElement.GetNamedArray("events"); foreach (var elem in eventElements) { var title = elem.GetObject().GetNamedString("title"); var started_at = elem.GetObject().GetNamedString("started_at"); var place = elem.GetObject().GetNamedString("place"); var event_url = elem.GetObject().GetNamedString("event_url"); var item = new EventDataItem(title, started_at, place, event_url); Dispatcher.InvokeAsync(CoreDispatcherPriority.Normal, (s, a) => { _source.Items.Add(item); }, this, null); } }
GetStringAsync に続けて ConfigureAwait(false) の呼び出しを行っています。
これで、以降の処理は UI スレッドではなく別スレッドで実行されるようになります。
UI スレッド以外で動作するようになったため、最後の _source.Items.Add(item) は
Dispatcher を使って UI スレッドで動作するようにする必要があります。
非同期動作に関しては MSDN マガジンに詳しい説明が載っていますので、詳細はそちらを参考にして下さい。
http://msdn.microsoft.com/ja-jp/magazine/hh456402.aspx