sync over async и ConfigureAwait(false)
От: MadHuman Россия  
Дата: 02.01.23 15:01
Оценка:
С Новым Годом!)

В asp.net приложении на .net 4.8 есть синхронный акшн, в глубинах реализации (через длинную цепочку вызовов) надо использовать асинхронный метод (c кивордом async который).
упрощённый пример ситуации ниже

    static async Task<bool> DoWork(string msg) {
        //some work
        await Task.Delay(1000); //так виснет
        await Task.Delay(1000).ConfigureAwait(false); //так норм!
        return true;
    }
        

    //метод контроллера
    public void SomeControllerAction() {
        var t = DoWork("test");
        var r = t.Result; //тут виcнет
        //other work
    }


дак вот, при вызове у таски, возвращённой от async функции, .Result — на ней виснет и управление не возвращается.
виснет после 1-го вызова await в async функции.
суть проблемы раскопал — оказывается по умолчанию, после await, система пытается возвратить управление в исходный поток из которого был вызван await
но, исходный поток заблокирован на .Result — deadlock

в качестве лечения предлагается — после вызова таски для await добавлять .ConfigureAwait(false)
это типа не принуждает после await-а продолжить выполнение на исходном потоке.


такое поведение вызывает вопросы
1. зачем такое дефолтное поведение в asp.net? ведь при async/await-ах и так подразумевается что после окончания ожидания управление может продолжено свободным потоком из пула.
идея продолжения в исходном хуже и с точки зрения перфа — исходный поток мог уже быть назначен для выполнения другой таски и занят.

понятен кейс такого поведения — в UI приложениях, актуально чтоб продолжили в том же потоке откуда было вызвано, но для этого
в таких случаях можно и вызывать .ConfigureAwait(true) или даже вызывать только на рутовой таске, а вложенные наследуют заданное поведение (await происходит же из таски, и можно получить доступ к её опциям).
либо какую-то глобальную настройку выставлять в UI приложениях.
в asp.net так не надо, т.е. из-за частного случая сделали неудобное поведение.

2. почему нет глобальной настройки? или для рутовой таски указать стратегию для вложенных await-ах?

3. зачем об этом думать при каждом await ? а .ConfigureAwait(false) надо вызвать при каждом во всей цепочке


это какое-то неудачно решение дизайна?
или есть какие-то причины которые от меня ускользнули?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.