死锁与异步 - 不是标准的死锁

时间:2018-05-24 10:00:48

标签: c# asynchronous deadlock

我调用异步方法和死锁。现在我不知道为什么会这样。我不认为这是一个标准的死锁,因为我在Main方法中调用Task.Result - NOT IN UI线程。而且,我还尝试使用异步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()

我希望这是可读的。

这条路很好用。有趣的是,GetPremisesForLoggedUserGetLoggedAppUser部分融合,看起来像这样:

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?

1 个答案:

答案 0 :(得分:1)

好的,感谢@usr和@Evk我找到了这个bug。然而,这是标准的UI僵局,但更多的伪装。

在我致电GetPremisesForLoggedUser()之前,我正在使用工厂(IoC Container)创建表单:

IPremisePickerView view = objFactory.Resolve<IPremisePickerView>();
var premises = await GetPremisesForLoggedUser();

创建表单时,此表单所属的线程将自动成为UI线程。我认为这是在Form类中完成的。

事实证明我正在打电话     等待GetPremisesForLoggedUser()

UI线程上的

。所以可能所有任务都开始在UI线程上运行(从某些角度来看)。但这是否意味着在Main中调用的Task.Run.Result阻止了UI线程等待结果?我不确定,因为这是在主线程上调用的(在这种情况下不是UI线程)。所以,如果有人能够弥补我的答案,那就太好了。

无论如何,在调用异步方法之后移动表单创建之后,一切都开始工作了。