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

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

我已阅读:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.htmldeadlock even after using ConfigureAwait(false) in Asp.Net flow 上接受的答案,但我太密集了,看不出发生了什么。

我有代码:

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 被声明为异步的。我认为(尤其是从上面的第一篇文章)如果我使用 ConfigureAwait(false) 我可以调用异步方法而不会出现死锁。但这就是我似乎得到的。对“CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult() 的调用从不 returns!

我正在使用 GetAwaiter.GetResult 因为 CancelCalibration 不是异步方法。这是一个通过 MVVM 模式定义的按钮处理程序:

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

如果有人要告诉我可以使 CancelCalibration 异步,请告诉我怎么做。我可以只在方法声明中添加 async 吗?但是,我仍然想知道为什么 ConfigureAwait.GetAwaiter.GetResult 模式给我带来麻烦。我的理解是 GetAwaiter.GetResult 是一种在无法更改签名时从同步方法调用异步方法的方法。

我猜我并没有真正摆脱使用原始上下文的束缚,但我做错了什么以及修复它的模式是什么? 谢谢, 戴夫

I thought (especially from the first article above) that if I use ConfigureAwait(false) I could call an async method without a deadlock.

那篇文章中有一个重要说明:

Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. 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. Using ConfigureAwait(false) to avoid deadlock is at best just a hack).

那么,ConfigureAwait(false)是否用于传递闭包中的everyawait?这意味着:

  • 是否 CloseLoadDoor 对每个 await 使用 ConfigureAwait(false)?我们可以从发布的代码中看到它确实如此。
  • 是否 _model.CloseLoadDoor 对每个 await 使用 ConfigureAwait(false)?我们看不到的。
  • 是否每个 _model.CloseLoadDoor 调用的方法都对每个 await 使用 ConfigureAwait(false)
  • 是否每个 _model.CloseLoadDoor 调用的每个方法调用的每个方法都对每个 await 使用 ConfigureAwait(false)
  • 等等

这至少是一个沉重的维护负担。我怀疑在调用堆栈的某个地方,缺少 ConfigureAwait(false).

正如该说明的结论:

As the title of this post points out, the better solution is “Don’t block on async code”.

换句话说,那篇文章的重点是"Don't Block on Async Code"。这不是说 "Block on Async Code with This One Neat Trick".

如果您确实想要 API 同时支持同步和异步调用者,我建议在 my article on brownfield async.

中使用 bool 参数 hack

附带说明一下,在代码 CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult() 中,ConfigureAwait 不执行任何操作。是 "configure await",不是 "configure task"。由于那里没有 await,因此 ConfigureAwait(false) 无效。