我调用异步方法和死锁。现在我不知道为什么会这样。我不认为这是一个标准的死锁,因为我在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()
我希望这是可读的。
这条路很好用。有趣的是,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?
答案 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线程)。所以,如果有人能够弥补我的答案,那就太好了。
无论如何,在调用异步方法之后移动表单创建之后,一切都开始工作了。