11

私は最初にコードを置き、次に状況を説明し、それに基づいて私の質問をしたいと思います:

public partial class MainWindow : Window {

    public MainWindow() {
        InitializeComponent();
    }

    private async void Button_Click_2(object sender, RoutedEventArgs e) {

        var result = await GetValuesAsync();
        Foo.Text += result;
    }

    public async Task<string> GetValuesAsync() {           

        using (var httpClient = new HttpClient()) {

            var response = await httpClient
                .GetAsync("http://www.google.com")
                .ConfigureAwait(continueOnCapturedContext: false);


            // This is the continuation for the httpClient.GetAsync method.
            // We shouldn't get back to sync context here
            // Cuz the continueOnCapturedContext is set to *false*
            // for the Task which is returned from httpClient.GetAsync method
            var html = await GetStringAsync();

            // This is the continuation for the GetStringAsync method.
            // Should I get back to sync context here?
            // Cuz the continueOnCapturedContext is set to *true*
            // for the Task which is returned from GetStringAsync 

            // However, GetStringAsync may be executed in another thread
            // which has no knowledge for the sync context 
            // because the continueOnCapturedContext is set to *false*
            // for the Task which is returned from httpClient.GetAsync method.

            // But, on the other hand, GetStringAsync method also has a 
            // chance to be executed in the UI thread but we shouldn't be
            // relying on that. 
            html += "Hey...";
            Foo.Text = html;

            return html;
        }
    }

    public async Task<string> GetStringAsync() {

        await Task.Delay(1000);
        return "Done...";
    }
}

これは、.NET 4.5で実行される非常に単純なWPFサンプルであり、おそらくあまり意味がありませんが、これは私の状況を説明するのに役立つはずです。

画面に非同期クリックイベントのあるボタンがあります。GetValuesAsyncコードを見ると、キーワードawaitの使用法が2回わかります。最初の使用では、メソッドcontinueOnCapturedContextのパラメーターをに設定しました。したがって、これは、継続が内で実行されることを必ずしも望んでいないことを示しています。ここまでは順調ですね。Task.ConfigureAwaitfalseSynchronizationContext.Current

await(メソッドを使用した) 2回目の使用では、GetStringAsyncメソッドを呼び出しませんでしたConfigureAwait。したがって、基本的に、メソッドを継続するために現在の同期コンテキストに戻りたいGetStringAsyncことを示しました。ご覧TextBlock.Textのとおり、継続内に(UIスレッドに属する)プロパティを設定しようとしています。

アプリケーションを実行してボタンをクリックすると、例外が発生し、次のメッセージが表示されます。

別のスレッドがオブジェクトを所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。

最初はこれは意味がなく、バグを発見したと思いましたがGetStringAsync、UIスレッドとは異なり、同期コンテキストの知識がない別のスレッド(可能性が高い)で実行される可能性があることに気付きました。メソッドから返されるcontinueOnCapturedContextはに設定されfalseます。TaskhttpClient.GetAsync

これはここに当てはまりますか?また、この場合、GetStringAsynchttpClient.GetAsyncメソッドの継続がUIスレッド内で実行される可能性があるため、メソッドがUIスレッドにポストバックされる可能性はありますか?

コード内にもいくつかコメントがあります。私の質問とコード内のコメントを考慮して、ここで何かが欠けていますか?

4

1 に答える 1

11

を呼び出すと、実行中のメソッドがすでに完了していない限りConfigureAwait(false)残りのメソッドはスレッドプールスレッドで実行されます。Taskawait

ほぼ確実に非同期で実行されるため、スレッドプールスレッドで実行するGetAsyncことを期待します。GetStringAsync

public async Task<string> GetValuesAsync() {           

    using (var httpClient = new HttpClient()) {

        var response = await httpClient
            .GetAsync("http://www.google.com")
            .ConfigureAwait(continueOnCapturedContext: false);

        // And now we're on the thread pool thread.

        // This "await" will capture the current SynchronizationContext...
        var html = await GetStringAsync();
        // ... and resume it here.

        // But it's not the UI SynchronizationContext.
        // It's the ThreadPool SynchronizationContext.
        // So we're back on a thread pool thread here.

        // So this will raise an exception.
        html += "Hey...";
        Foo.Text = html;

        return html;
    }
}

また、この場合、httpClient.GetAsyncメソッドの継続がUIスレッド内で実行される可能性があるため、GetStringAsyncメソッドがUIスレッドにポストバックされる可能性はありますか?

GetStringAsyncUIスレッドで実行される唯一の方法は、GetAsync実際にawait編集される前に完了した場合です。可能性は非常に低いです。

このため、コンテキストが不要になるたびConfigureAwait(false)に使用することを好みます。 await

于 2012-10-17T11:44:46.517 に答える