SynchronizationContext

SynchronizationContext

阅读Stephen Toub's article on SynchronizationContext之后,我对这个.NET 4.5代码的输出有一个疑问:

private void btnDoSomething_Click()
{
    LogSyncContext("btnDoSomething_Click");
    DoItAsync().Wait();
}
private async Task DoItAsync()
{
    LogSyncContext("DoItAsync");
    await PerformServiceCall().ConfigureAwait(false); //to avoid deadlocking
}
private async Task PerformServiceCall()
{
    LogSyncContext("PerformServiceCall 1");
    HttpResponseMessage message = await new HttpClient
    {
        BaseAddress = new Uri("http://my-service")
    }
    .GetAsync("/").ConfigureAwait(false); //to avoid deadlocking
    LogSyncContext("PerformServiceCall 2");
    await ProcessMessage(message);
    LogSyncContext("PerformServiceCall 3");
}

private async Task ProcessMessage(HttpResponseMessage message)
{
    LogSyncContext("ProcessMessage");
    string data = await message.Content.ReadAsStringAsync();
    //do something with data
}

private static void LogSyncContext(string statementId)
{
    Trace.WriteLine(String.Format("{0} {1}", statementId, SynchronizationContext.Current != null ? SynchronizationContext.Current.GetType().Name : TaskScheduler.Current.GetType().Name));
}

输出为:



但我希望PerformServiceCall 1不在WindowsFormsSynchronizationContext上,因为本文指出“SynchronizationContext.Current不会“流过”等待点” ...

使用Task.Run和异步lambda调用PerformServiceCall时,上下文不会传递,如下所示:
await Task.Run(async () =>
{
    await PerformServiceCall();
}).ConfigureAwait(false);

任何人都可以澄清或指向有关此问题的一些文档吗?

最佳答案

Stephen的文章解释说SynchronizationContext不会像ExecutionContext那样“流动”(尽管SynchronizationContextExecutionContext的一部分)。ExecutionContext总是流。即使当您使用Task.Run时,如果SynchronizationContext随它一起流动,那么Task.Run也将在UI线程上执行,因此Task.Run将毫无意义。SynchronizationContext不会流动,而是在到达异步点(即await)并将其发布到该点后的延续时被捕获(除非另有明确说明)。
此报价中说明了不同之处:

这意味着在您的情况下,当您输出PerformServiceCall 1时,当前SynchronizationContext的确是WindowsFormsSynchronizationContext,因为您尚未到达任何异步点,并且您仍在UI线程中(请注意,await方法中第一个async之前的部分是在调用线程上同步执行,因此LogSyncContext("PerformServiceCall 1");ConfigureAwait(false)返回的任务上发生PerformServiceCall之前发生。
使用SynchronizationContext(不考虑捕获的ConfigureAwait(false))时,您只能“下车” UI的SynchronizationContext。第一次发生在HttpClient.GetAsync上,然后再次在PerformServiceCall上。

关于c# - SynchronizationContext在Task.Run上流动,但不在等待中流动,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29385425/

10-13 06:46