根据this link:
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线程吗? 最佳答案
我用in my own blog post完整地解释了这一点,但在这里重申一下...
默认情况下,await
将捕获当前的“上下文”,并在该上下文上恢复其async
方法。除非是SynchronizationContext.Current
,否则此上下文为null
,在这种情况下为TaskScheduler.Current
。
当您一次只有一个线程的SynchronizationContext
并阻塞了代表异步代码的任务(例如,使用Task.Wait
或Task<T>.Result
)时,可能会发生死锁。请注意,导致死锁的是阻塞,而不仅仅是SynchronizationContext
;适当的解决方案(几乎总是)是使调用代码异步化(例如,将Task.Wait
/Task<T>.Result
替换为await
)。在ASP.NET上尤其如此。
您的示例在ASP.NET上运行;没有UI线程。
当前的SynchronizationContext
应该是AspNetSynchronizationContext
的实例,它是表示ASP.NET请求的上下文。此上下文一次只允许一个线程进入。
因此,遍历您的示例:
当请求执行此操作时,CarsSync
将在该请求上下文中开始执行。前进至此行:
var cars = client.GetCarsInAWrongWayAsync().Result;
基本上与此相同:
Task<IEnumerable<Car>> carsTask = client.GetCarsInAWrongWayAsync();
var cars = carsTask.Result;
因此,它将继续调用
GetCarsInAWrongWayAsync
,该命令将一直运行直到遇到第一个await
(即GetAsync
调用)。此时,GetCarsInAWrongWayAsync
捕获其当前上下文(ASP.NET请求上下文),并返回不完整的Task<IEnumerable<Car>>
。 GetAsync
下载完成后,GetCarsInAWrongWayAsync
将继续在该ASP.NET请求上下文中执行,并(最终)完成已返回的任务。但是,
GetCarsInAWrongWayAsync
一旦返回未完成的任务,CarsSync
就会阻止当前线程,等待该任务完成。请注意,当前线程位于该ASP.NET请求上下文中,因此CarsSync
将阻止GetCarsInAWrongWayAsync
继续执行,从而导致死锁。最后一点,
GetCarsInAWrongWayAsync
是可以的方法。如果使用ConfigureAwait(false)
会更好,但是实际上并没有错。 CarsSync
是导致死锁的方法;它对Task<T>.Result
的调用是错误的。适当的解决方法是更改CarsSync
:public class HomeController : Controller
{
public async Task<ViewResult> CarsSync()
{
SampleAPIClient client = new SampleAPIClient();
var cars = await client.GetCarsInAWrongWayAsync();
return View("Index", model: cars);
}
}
关于c# - 使用SynchronizationContext时async/await死锁,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34151179/