我已经阅读了:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html和即使在Asp.Net流中使用ConfigureAwait(false)后,死锁时也可以接受的答案,但它太密集了,看不清发生了什么。
我有代码:
private void CancelCalibration()
{
// ...
TaskResult closeDoorResult = CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult();
CalibrationState = CalibrationState.Idle;
return;
// ...
}
private async Task<TaskResult> CloseLoadDoor()
{
TaskResult result = await _model.CloseLoadDoor().ConfigureAwait(false);
return result;
}
public async Task<TaskResult> CloseLoadDoor()
{
TaskResult result = new TaskResult()
{
Explanation = "",
Success = true
};
await _robotController.CloseLoadDoors().ConfigureAwait(false);
return result;
}
public async Task CloseLoadDoors()
{
await Task.Run(() => _robot.CloseLoadDoors());
}
public void CloseLoadDoors()
{
// syncronous code from here down
_doorController.CloseLoadDoors(_operationsManager.GetLoadDoorCalibration());
}
正如您所看到的,CloseLoadDoor被声明为async。我认为(尤其是在上面的第一篇文章中),如果我使用ConfigureAwait(false),我可以在没有死锁的情况下调用异步方法。但这似乎是我得到的。对"CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult()的调用永远不会返回!
我使用GetAwaiter.GetResult是因为CancelCalibration不是一个异步方法。这是一个通过MVVM模式定义的按钮处理程序:
public ICommand CancelCalibrationCommand
=> _cancelCalibrationCommand ?? (_cancelCalibrationCommand = new DelegateCommand(CancelCalibration));
如果有人要告诉我我可以使CancelCalibration异步,请告诉我如何。我可以将async
添加到方法声明中吗?然而,我仍然想知道为什么ConfigureAwait.GetAwaiter.GetResult
模式会给我带来麻烦。我的理解是,当不能更改签名时,GetAwaiter.GetResult
是从同步方法调用异步方法的一种方式。
我想我并没有真正从使用原始上下文中解放出来,但我做错了什么,修复它的模式是什么?谢谢Dave
我认为(尤其是在上面的第一篇文章中),如果我使用ConfigureAwait(false),我可以在没有死锁的情况下调用异步方法。
这篇文章中有一个重要的注释:
使用ConfigureAwait(false)来避免死锁是一种危险的做法。您必须对阻塞代码调用的所有方法的传递闭包中的每个等待使用ConfigureAwait(false),包括所有第三方和第二方代码。使用ConfigureAwait(false)来避免死锁充其量只是一种破解)。
那么,ConfigureAwait(false)
是否用于传递闭包中的每个await
?这意味着:
CloseLoadDoor
是否对每个await
使用ConfigureAwait(false)
?我们可以从发布的代码中看到它确实如此_model.CloseLoadDoor
是否对每个await
使用ConfigureAwait(false)
?我们看不见_model.CloseLoadDoor
调用的每个方法是否对每个await
都使用ConfigureAwait(false)
_model.CloseLoadDoor
调用的每个方法是否都对每个await
使用ConfigureAwait(false)
- 等等
这至少是一个严重的维护负担。我怀疑在调用堆栈的某个地方,有一个丢失的ConfigureAwait(false)
。
正如该注释所总结的:
正如这篇文章的标题所指出的,更好的解决方案是"不要阻塞异步代码"。
换句话说,这篇文章的重点是"不要阻塞异步代码"。它并不是说"用这一个巧妙的技巧阻止异步代码"。
如果您确实想拥有一个同时支持同步和异步调用方的API,我建议在我关于brownfield async的文章中使用bool参数破解。
附带说明一下,在代码CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult()
中,ConfigureAwait
什么都不做。它是"配置等待",而不是"配置任务"。因为那里没有await
,所以ConfigureAwait(false)
没有任何作用。