c# - Why access Task.Result in the synchronous mode doesn't cause a deadlock?
问题描述
As we all know to access the Result property of Task in the UI thread and synchronous mode will deadlock.
As theoretical follow code will deadlock but not. Can you please explain why?
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
//MVC action
public ActionResult Index()
{
var result = System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result; // deadlock is expectation but not :(
...
}
I thought that System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result
is simialar to GetJsonAsync(...).Result
in some way, but not.
GetJsonAsync(...)).Result
will deadlock
but System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result
is not.
解决方案
Result
本身不会导致死锁。如果该任务也await
需要该上下文,则从单线程上下文调用时会导致死锁。
更多详情:
await
默认情况下捕获一个上下文并在该上下文上恢复。(您可以使用ConfigureAwait(false)
覆盖此默认行为并改为在线程池线程上恢复。)Result
阻塞当前线程直到Task
完成。(您可以使用await
异步使用任务以避免阻塞线程。)- 一些上下文是单线程上下文;即,它们一次只允许一个线程。例如,ASP.NET Classic 具有单线程请求上下文。(您可以使用
Task.Run
线程池上下文在线程池线程上运行代码,该线程池上下文不是单线程上下文。)
因此,要获得死锁,您需要有一个await
捕获单线程上下文的线程,然后在该上下文中阻塞一个线程(例如,调用Result
该任务)。await
需要上下文来完成Task
,但上下文一次只允许一个线程,并且在该Result
上下文中保持线程阻塞直到Task
完成。
在您的示例中,您GetJsonAsync
在 a 内部调用Task.Run
,它在线程池上运行它。因此await
in GetJsonAsync
(以及await
传递给的委托中的 in Task.Run
)捕获线程池上下文,而不是 ASP.NET 请求线程上下文。然后您的代码调用Result
,它确实阻塞了 ASP.NET 请求线程(及其上下文),但由于await
不需要该上下文,因此没有死锁。
推荐阅读
- twitter-bootstrap - offset-sm 遇到问题(引导程序 4)
- android - Android onQueryTextChange 不显示 UI 更改
- excel - 从列表中删除行
- php - 在 Laravel 中如何管理嵌套的身份验证系统?
- android-studio - 版本 28 是旧版支持的最新版本
- c# - Google Drive API C# 应用程序名称不变
- python - 如何以 django 形式上传 python 脚本并将其用于某些处理?
- xml - 替换/删除 XML 文件行
- c++ - 为什么 Visual Studio 对我的 .lib 项目中的引用显示警告?
- c# - 无法从 WebResponse 流创建 System.Drawing.Image 对象