当您使用await关键字等待方法时,编译器会代表您生成大量代码。其中一个目的是处理与UI线程的同步。这个功能的关键组件是
SynchronizationContext.Current
,它获取当前线程的同步上下文。根据您所处的环境,SynchronizationContext.Current
会有不同的值。Task的GetAwaiter
方法查找SynchronizationContext.Current
。如果当前同步上下文不为空,则传递给该awaiter的继续操作将被发布回该同步上下文。
如果以阻塞方式使用新的异步语言特性消耗方法,并且有可用的SynchronizationContext
,则会出现死锁。 当您以阻塞方式使用这样的方法(使用Wait方法等待任务或直接从任务的Result属性中取结果)时,同时会阻塞主线程。最终,在线程池中完成该方法内的任务时,它将调用继续操作以发布到主线程,因为已经捕获了SynchronizationContext.Current
。但是这里存在一个问题:UI线程被阻塞并且您陷入了死锁! public class HomeController : Controller
{
public ViewResult CarsSync()
{
SampleAPIClient client = new SampleAPIClient();
var cars = client.GetCarsInAWrongWayAsync().Result;
return View("Index", model: cars);
}
}
public class SampleAPIClient
{
private const string ApiUri = "http://localhost:17257/api/cars";
public async Task<IEnumerable<Car>> GetCarsInAWrongWayAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(ApiUri);
// Not the best way to handle it but will do the work for demo purposes
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<IEnumerable<Car>>();
}
}
}
我不太理解上面加粗的陈述,但是当我测试了上面的代码后,它像预期的一样死锁了。但我仍然不明白为什么UI线程被阻塞了?
在这种情况下,有哪些可用的
SynchronizationContext
?它是UI线程吗?
Task<ViewResult>
,它可以完全异步化(这在 ASP.NET 线程使用方面具有更好的可扩展性)。 - Richard Szalay