Страницы

Поиск по вопросам

воскресенье, 8 марта 2020 г.

Синхронный и асинхронный методы и дублирование кода

#c_sharp #async_await #рефакторинг


Здравствуйте. У меня есть два очень похожих метода, один обычный а другой асинхронный.
Выглядят они так: 

public async Task> GetDataAsync()
{            
    var types = _listCache.Get(CacheKey);
    if (types == null)
    {
        types = await GetDataAsync(_settings.Url, Converter);
        _listCache.Add(CacheKey, types);
    }
    return types;
}

public List GetData()
{            
    var types = _listCache.Get(CacheKey);
    if (types == null)
    {
        types = GetData(_settings.Url, Converter);
        _listCache.Add(CacheKey, types);
    }
    return types;
}


Можно заметить что методы почти одинаковы и код в них дублируется чего хотелось бы
избежать, Можно ли как-то сделать это безболезненно? Будут ли например дедлоки если
синхронную версию написать таким образом: 

public List GetData()
{                                
    return GetDataAsync().Result;
}

    


Ответы

Ответ 1



Да, дедлоки будут. Допустим, вы вызываете в UI-потоке GetDataAsync().Result. При этом код выполняется синхронно до момента await GetDataAsync(_settings.Url, Converter);, и далее возвращается в синхронное ожидание выдачи результата Result. Что происходит, когда отрабатывает await? Код пытается доставить continuation в основной поток, но поток заблокирован до получения результата. Вот вам и дедлок. Не пытайтесь предоставить API «на все случаи жизни». Если функция по своей сути асинхронная, предоставляйте только асинхронный интерфейс. Если функция по своей сути синхронная (например, она не ожидает внешних событий, наподобие чтения файла или прихода информации по сети), предоставляйте только синхронный интерфейс. Если синхронный клиент пользуется по сути асинхронным интерфейсом, он по-хорошему должен стать сам асинхронным. Ну или если хочется костыль, то выгрузите в фоновый поток: Task.Run(<асинхронная функция>).Result. Ещё по теме: https://blogs.msdn.microsoft.com/pfxteam/2012/04/13/should-i-expose-synchronous-wrappers-for-asynchronous-methods/

Ответ 2



У подобного решения три проблемы. Программа повиснет, если такой код будет выполняться в потоке UI или любом другом однопоточном контексте. Подробнее о причинах и как бороться - тут: Зависает оператор `await` в оконном приложении / программа висит при вызове Task.Result или Wait Вылетевшее исключение обернется в AggregateException, что сделает некрасивым стек вызовов, и затруднит классификацию исключений в блоках catch. Вызываемые методы GetDataAsync и GetData могут работать по-разному.

Комментариев нет:

Отправить комментарий