非同期メソッド呼び出し後のスレッドについて

 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