Все еще запутался в ConfigureAwait(false), используемом с GetAwaiter и GetResult в C#. Получение тупика или метод, не возвращающийся

Я прочитал: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html и принятый ответ в тупике даже после использования ConfigureAwait(false) в потоке Asp.Net, но я просто слишком плотно, чтобы увидеть, что происходит.

У меня есть код:

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

Если кто-то скажет мне, что я могу сделать асинхронную отмену калибровки, скажите, пожалуйста, как. Могу ли я просто добавить async к объявлению метода? ОДНАКО, я все еще хотел бы знать, почему ConfigureAwait.GetAwaiter.GetResult шаблон доставляет мне неприятности. Я понимаю, что GetAwaiter.GetResult был способ вызвать асинхронный метод из синхронных методов, когда изменение подписи не вариант.

Я предполагаю, что на самом деле я не освобождаю себя от использования исходного контекста, но что я делаю не так и каков порядок, чтобы это исправить? Спасибо дэйв

1 ответ

Решение

Я подумал (особенно из первой статьи выше), что если я использую 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-интерфейс, поддерживающий как синхронные, так и асинхронные вызовы, я рекомендую использовать метод взлома аргументов bool в моей статье об асинхронном режиме brownfield.


На заметку, в коде CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult(), ConfigureAwait ничего не делает Это "настроить ожидание", а не "настроить задачу". Так как нет await там, ConfigureAwait(false) не имеет никакого эффекта

Другие вопросы по тегам