在C#中与GetAwaiter和GetResult一起使用的ConfigureAwait(false)仍然很混乱。获取死锁或方法未返回
我读过:和 当时的答案是可以接受的,但我太过密集,看不清到底发生了什么 我有密码:在C#中与GetAwaiter和GetResult一起使用的ConfigureAwait(false)仍然很混乱。获取死锁或方法未返回,c#,asynchronous,async-await,configureawait,C#,Asynchronous,Async Await,Configureawait,我读过:和 当时的答案是可以接受的,但我太过密集,看不清到底发生了什么 我有密码: private void CancelCalibration() { // ... TaskResult closeDoorResult = CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult(); CalibrationState = CalibrationState.Idle; return;
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());
}
如果有人要告诉我,我可以使CancelCalibration异步,请告诉我如何。我可以将async
添加到方法声明中吗?然而,我仍然想知道为什么ConfigureAwait.GetAwaiter.GetResult
模式给我带来麻烦。我的理解是,GetAwaiter.GetResult
是一种在无法更改签名时从同步方法调用异步方法的方法
我猜我并没有真正从使用原始上下文中解脱出来,但我做错了什么?修复它的模式是什么?
谢谢
戴夫
我认为(特别是在上面的第一篇文章中),如果我使用ConfigureAwait(false),我可以在没有死锁的情况下调用异步方法
那篇文章中有一个重要的提示:
使用ConfigureWait(false)避免死锁是一种危险的做法。对于阻塞代码调用的所有方法(包括所有第三方和第二方代码)的传递闭包中的每个等待,都必须使用ConfigureAwait(false)。使用ConfigureAwait(false)来避免死锁充其量只是一种攻击)
那么,ConfigureAwait(false)
是否用于传递闭包中的每个await
?这意味着:
是否对每个CloseLoadDoor
使用wait
?我们可以从发布的代码中看出它确实如此configurewait(false)
是否为每个\u model.CloseLoadDoor
等待
使用
?我们看不见的配置等待(false)
\u model.CloseLoadDoor调用的每个方法是否对每个
等待
使用
配置等待(false)
- 由
\u model.CloseLoadDoor调用的每个方法调用的每个方法是否都对每个
等待
使用
配置等待(false)
- 等等
ConfigureAwait(false)
正如该说明得出的结论:
正如这篇文章的标题所指出的,更好的解决方案是“不要阻塞异步代码”
换句话说,这篇文章的重点是“不要阻塞异步代码”。它并不是说“用这个巧妙的技巧阻止异步代码”
如果您确实希望有一个同时支持同步和异步调用方的API,我建议您在中使用bool参数hack
另一方面,在代码
CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult()
中,ConfigureAwait
没有任何作用。它是“配置等待”,而不是“配置任务”。由于那里没有wait
,因此configurewait(false)
没有任何效果。所显示的代码没有死锁(模拟\u model.CloseLoadDoor()
和Task.Delay()
)。如果您使用CancelCalibration
作为委托来命令它是一个逻辑事件处理程序,并且可以标记为async void
,这将允许您wait CloseDoor()
此异常包括逻辑上是事件处理程序的方法,即使它们不是真正的事件处理程序(例如,ICommand.Execute实现)有一种模式,不要编写无法调试的代码。异步的常见问题是,你不知道是否应该归咎于钉子的锤子。谢谢GSerg。我认为_model.CloseLoadDoor会立即返回。在本例中,这可能是正确的做法,但我想知道如何等待门被打开从未标记为async的函数关闭。谢谢!谢谢Stephen。我希望您能检查这个问题。我编辑了代码以显示调用的其他方法。它们都使用“ConfigureAwait(false)”,除非我们切换到同步代码并使用await Task.Run(()=>\u robot.CloseLoadDoors());没有ConfigureAwait。这可能是问题吗?Stephen,我被你的“旁注”评论弄糊涂了。也许我使用的是ConfigureAwait().GetAwaiter().GetResult()不正确?我的意图是等待CloseLoadDoor的结果。也许我没有这样做?我需要在这个调用中等待吗?或者以另一种方式询问问题…如果我告诉您由于某种奇怪的原因不能使用“wait”,您将如何编写对CloseLoadDoor()的调用。ConfigureWait(false)。GetWaiter()。GetResult()正确地模拟使用等待?我不理解你的评论:这是配置等待,而不是配置任务”。谢谢!我重新阅读了你的文章:在我看来,如果我不能保证配置等待(false)在堆栈中一直使用,我使用任务会更安全。运行(()=>CloseLoadDoor().GetAwaiter().GetResult();!当然在我的情况下,我可以将我的top方法更改为async并使用Wait,但正如你的文章所指出的,有时这可能无法完成。谢谢!@Dave:是的,错过了等待任务的配置等待(false)
。运行(…);
肯定会导致这里的死锁。配置等待(false)的问题.GetAwaiter().GetResult()
是指.ConfigureAwait(false)
在那里没有意义;它与没有ConfigureAwait(false)的.GetAwaiter().GetResult()
完全相同
。对于异步同步,没有完美的解决方案,但将其包装在任务中。运行
并阻止它将适用于大多数代码。感谢Stephen。我现在非常清楚。在现实世界中,这仍然是一个非常困难的问题,因为人们无法始终保证“等待…配置等待(false)”“一直以来都在使用。人们甚至可能看不到代码(隐藏在库中)或由于某种原因无法更改它。
public ICommand CancelCalibrationCommand
=> _cancelCalibrationCommand ?? (_cancelCalibrationCommand = new DelegateCommand(CancelCalibration));