Все еще запутался в 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)
не имеет никакого эффекта