【发布时间】:2018-05-24 10:00:47
【问题描述】:
我称之为异步方法和死锁。现在我不知道为什么会这样。我不认为这是一个标准的死锁,因为我在 Main 方法中调用 Task.Result - 而不是在 UI 线程中。更重要的是,我还尝试使用 async Main 而不调用 Task。但结果是一样的。
问题在于 WinForms 项目。一切都从 Main 方法开始:
bool res = Task.Run(() => contr.RegisterPremiseAllInOne()).Result;
这是在新任务中对异步方法 RegisterPremiseAllInOne 的简单调用。
接下来会发生什么... RegisterPremiseAllInOne 的工作方式更像这样(简化模型):
public async Task<bool> RegisterPremiseAllInOne()
{
AppUser loggedAppUser = await appUserContr.GetLoggedAppUser(); // <-- everything works fine here
if(loggedAppUser == null)
return false;
var premises = await GetPremisesForLoggedUser(); //at the end there is a deadlock
}
我现在将向您展示每个方法的调用做了什么(一切都以对 WebApi 的其余查询结束):
GetLoggedAppUser ->
await API.Users.GetLoggedAppUser() ->
await ClientHelper.GetObjectFromRequest<Appuser>("AppUsers/logged") ->
await SendAsyncRequest() ->
await HttpClient.SendAsync()
我希望这很容易阅读。
这条路径运行良好。有趣的是,GetPremisesForLoggedUser 与GetLoggedAppUser 部分收敛,看起来像这样:
GetPremisesForLoggedUser ->
await API.Premises.GetForLoggedUser() ->
await ClientHelper.GetListFromRequest<premise>("premises/logged") ->
await SendAsyncRequest() ->
await HttpClient.SendAsync()
如您所见,有一条笔直的路径。调用 API 并最终发送一个 http 请求。
死锁在SendAsync 和HttpClient 内。我不知道为什么。然而,第一条路径工作正常。
在服务器端一切正常。服务器返回成功响应。但是客户端挂了。
我认为它应该有效。我不会将同步代码与异步代码混在一起。所有方法都返回Task<T> 并标记为异步。
也许有人知道问题可能出在哪里?也许你会想看一些调试窗口的截图:Parallel Stack / Tasks?
【问题讨论】:
-
除非您明确知道自己在做什么,否则不要在任务中使用
.Result或.Wait。如果您的第一行代码来自程序主代码,那么对于较新的 C# 版本,您可以使此方法也异步并改用 await。 -
这是标准的死锁问题。您对异步调用 (
Task.Run(() => contr.RegisterPremiseAllInOne())) 的阻塞 (.Result) -
好吧,我尝试了没有 Task.Result 的 async Main,结果是一样的。更重要的是我不在 UI 线程中调用 Result 所以它应该可以工作(并且它在不同的调用之前工作了几行)
-
对我来说,not 看起来像标准死锁吗。结果本身并不危险。标准死锁的出现是因为任务主体试图进入正在使用的同步上下文。 Task.Run 在没有同步上下文的情况下运行。该模式通常是安全的,如果无法真正修复,通常是标准死锁的推荐解决方法。
标签: c# asynchronous deadlock