阅读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
那样“流动”(尽管SynchronizationContext
是ExecutionContext
的一部分)。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/