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

时间:2019-01-25 20:35:31

标签: c# asynchronous async-await configureawait

我已阅读: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()永远不会返回!

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

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

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

我想我并没有真正从使用原始上下文中解放出来,但是我做错了什么?解决该问题的模式是什么? 谢谢, 戴夫

1 个答案:

答案 0 :(得分:2)

  

我认为(尤其是从以上第一篇文章中),如果我使用ConfigureAwait(false),则可以调用一个没有死锁的异步方法。

该文章中有一个重要说明:

  

使用ConfigureAwait(false)避免死锁是一种危险的做法。在阻塞代码包括所有第三方代码和第二方代码所调用的所有方法的传递性关闭中,您必须为每次等待使用ConfigureAwait(false)。使用ConfigureAwait(false)避免死锁充其量只是一种破解。)

因此,在传递闭包中,ConfigureAwait(false)是否用于每个 await?这意味着:

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

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

该注释的结论如下:

  

这篇文章的标题指出,更好的解决方案是“不要阻止异步代码”。

换句话说,该文章的重点是“不要阻止异步代码”。并不是说“用这个巧妙的技巧阻止异步代码”。

如果您确实想拥有一个支持同步和异步调用者的API,建议您在my article on brownfield async中使用bool参数hack。


请注意,在代码CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult()中,ConfigureAwait不会执行任何操作。它是“等待配置”,而不是“配置任务”。由于那里没有await,因此ConfigureAwait(false)无效。