2022-03-20 15:47:05
Дааа... Кажется, из последнего примера ребята действительно выжали максимум. Это был мастер-класс по code review на высоком уровне Если вы не успели пробежаться по комментариям в предыдущем посте, то сделайте это.
Итак, главную проблему, первым заметил подписчик Алексей. Он увидел, что ожидание выполнения всех задач (Task.WhenAll()) - расточительно. По логике метода необходимо:
1. Обратиться ко всем имеющимся адресам, которые возвращает метод GetReplicationUrls(). Нам предоставляют множество резервных адресов, так как подразумевается, что некоторые могут быть недоступны по самым разным причинам (неисправен, находится на техническом обслуживании и т. д.). Разумеется, какие адреса и в какой момент они будут нам доступны, нам никто не сообщит.
2. Получить цены.
3. Вернуть
одну цену.
Исходя из этой логики, ожидание всех цен абсолютно не требуется. Достаточно дождаться первой полученной цены и вернуть её. В этом нам поможет статический метод WhenAny(), который реализован в классе Task. Как только одна из задач будет завершена (цена получена), мы сразу же вернём её результат. В противном случае, мы будем вынуждены ждать столько времени, сколько занимает самая длительная задача, что увеличит время выполнения метода LoadItemPriceAsync(). Это будет особенно критично в тех случаях, когда будут сетевые задержки, таймауты и прочие сценарии, которые нельзя назвать успешными.
Кроме того, Алесей предложил интересный алгоритм в случае одновременного обращения множества задач к одному и тому же адресу. Почитайте в комментариях, это может оказаться полезным.
На этом можно было закончить, но подписчик Alexander Radchenko заметил, что производительность можно поднять еще выше. Александр очень точно написал эту мысль в комментарии, поэтому я приведу цитату:
"Для ещё более быстрого решения надо добавить CancellationToken
чтобы останавливать цикл создания задач, как только первая задача выполнена и даже возможно прерывание уже выполняемых задач."
Безусловно, в Enterprise разработке высоконагруженных систем, применить такой подход можно и нужно.
Завершил картину Руслан Петряев, предложив лаконичный вариант рефакторинга, который мне очень понравился:
private async Task
LoadItemPriceAsync(int itemId) =>
await Task.WhenAny(GetReplicationServersUrls()
.Select(async url => await LoadCurrentPriceAsync(itemId, url)))
.Result;
Здесь не подразумевается обработка исключений. Это вам на самостоятельную работу Главная суть полностью раскрыта!
Что же... Это была прекрасная командная работа. Всем нам очень повезло поучаствовать в этом полезном образовательном действе.
Спасибо!
2.1K views12:47