简体   繁体   English

仍然对与C#中的GetAwaiter和GetResult一起使用的ConfigureAwait(false)感到困惑。 使死锁或方法不返回

[英]Still confused on ConfigureAwait(false) used with GetAwaiter and GetResult in C#. Getting a deadlock or method not returning

I have read: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html and the accepted answer at deadlock even after using ConfigureAwait(false) in Asp.Net flow but am just too dense to see what is going on. 我已经读过: http : //blog.stephencleary.com/2012/07/dont-block-on-async-code.html死锁的公认答案, 即使在Asp.Net流中使用ConfigureAwait(false)之后,也只是太稠密,看不到发生了什么事。

I have code: 我有代码:

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());                
        }

As you can see, CloseLoadDoor is declared async. 如您所见,CloseLoadDoor被声明为异步。 I thought (especially from the first article above) that if I use ConfigureAwait(false) I could call an async method without a deadlock. 我以为(特别是从以上第一篇文章中)我可以通过使用ConfigureAwait(false)来调用一个没有死锁的异步方法。 But that is what I appear to get. 但这就是我似乎得到的。 The call to "CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult() never returns! 对“ CloseLoadDoor()。ConfigureAwait(false).GetAwaiter()。GetResult()的调用永远不会返回!

I'm using the GetAwaiter.GetResult because CancelCalibration is NOT an async method. 我正在使用GetAwaiter.GetResult,因为CancelCalibration不是异步方法。 It's a button handler defined via an MVVM pattern: 它是通过MVVM模式定义的按钮处理程序:

public ICommand CancelCalibrationCommand
        => _cancelCalibrationCommand ?? (_cancelCalibrationCommand = new DelegateCommand(CancelCalibration));

If someone is going to tell me that I can make CancelCalibration async, please tell me how. 如果有人要告诉我可以使CancelCalibration异步,请告诉我如何。 Can I just add async to the method declaration? 我可以将async添加到方法声明中吗? HOWEVER, I'd still like to know why the ConfigureAwait.GetAwaiter.GetResult pattern is giving me trouble. 但是,我仍然想知道为什么ConfigureAwait.GetAwaiter.GetResult模式给我带来麻烦。 My understanding was that GetAwaiter.GetResult was a way to call async method from syncronous methods when changing the signature is not an option. 我的理解是,当不能选择更改签名时, GetAwaiter.GetResult是一种从同步方法中调用异步方法的方法。

I'm guessing I'm not really freeing myself from using the original context, but what am I doing wrong and what is the pattern to fix it? 我猜我并没有真正从使用原始上下文中解放出来,但是我在做错什么,以及解决该问题的模式是什么? Thanks, Dave 谢谢戴夫

I thought (especially from the first article above) that if I use ConfigureAwait(false) I could call an async method without a deadlock. 我以为(特别是从以上第一篇文章中)我可以通过使用ConfigureAwait(false)来调用一个没有死锁的异步方法。

There's an important note in that article: 那篇文章有一个重要的说明:

Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. 使用ConfigureAwait(false)避免死锁是一种危险的做法。 You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code . 在阻塞代码调用的所有方法( 包括所有第三方代码和第二方代码 )的传递关闭中,您必须为每次等待使用ConfigureAwait(false)。 Using ConfigureAwait(false) to avoid deadlock is at best just a hack). 使用ConfigureAwait(false)避免死锁充其量只是一种破解)。

So, is ConfigureAwait(false) used for every await in the transitive closure? 那么,是否在传递闭包中为每个 await使用ConfigureAwait(false) This means: 这意味着:

  • Does CloseLoadDoor use ConfigureAwait(false) for every await ? CloseLoadDoor是否每次await都使用ConfigureAwait(false) We can see from the code posted that it does. 从发布的代码中我们可以看到它确实如此。
  • Does _model.CloseLoadDoor use ConfigureAwait(false) for every await ? _model.CloseLoadDoor是否每次await都使用ConfigureAwait(false) That we cannot see. 我们看不到。
  • Does every method called by _model.CloseLoadDoor use ConfigureAwait(false) for every await ? _model.CloseLoadDoor调用的每个方法是否都为每次await使用ConfigureAwait(false)
  • Does every method called by every method called by _model.CloseLoadDoor use ConfigureAwait(false) for every await ? _model.CloseLoadDoor调用的每个方法调用的每个方法是否都对每次await使用ConfigureAwait(false)
  • etc. 等等

This is a severe maintenance burden at least. 至少这是一个沉重的维护负担。 I suspect that somewhere down the call stack, there's a missing ConfigureAwait(false) . 我怀疑在调用堆栈的某个地方,缺少一个ConfigureAwait(false)

As that note concludes: 该注释总结如下:

As the title of this post points out, the better solution is “Don't block on async code”. 正如该帖子的标题所指出的那样,更好的解决方案是“不要阻止异步代码”。

In other words, the whole point of that article is "Don't Block on Async Code". 换句话说,该文章的重点是“不要阻止异步代码”。 It's not saying "Block on Async Code with This One Neat Trick". 这并不是说“使用这一巧妙技巧阻止异步代码”。

If you do want to have an API that supports both synchronous and asynchronous callers, I recommend using the bool argument hack in my article on brownfield async . 如果您确实想拥有一个同时支持同步和异步调用者的API,建议在我关于brownfield async的文章中使用bool参数hack。


On a side note, in the code CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult() , the ConfigureAwait doesn't do anything. 附带说明一下,在代码CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult()ConfigureAwait不会执行任何操作。 It's "configure await", not "configure task". 它是“等待配置”,而不是“配置任务”。 Since there's no await there, the ConfigureAwait(false) has no effect. 由于那里没有await ,因此ConfigureAwait(false)无效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM