Я переписываю старое приложение и пытаюсь использовать async
для его ускорения.
Старый код делал что-то вроде этого:
var value1 = getValue("key1");
var value2 = getValue("key2");
var value3 = getValue("key3");
где функция getValue
управляла собственным кешем в словаре, делая что-то вроде этого:
object getValue(string key) {
if (cache.ContainsKey(key)) return cache[key];
var value = callSomeHttpEndPointsAndCalculateTheValue(key);
cache.Add(key, value);
return value;
}
Если я делаю getValue
async
и await
каждый вызов getValue
, то все работает хорошо. Но она не быстрее старой версии, потому что все работает синхронно, как раньше.
Если я удалю await
(ну, если я отложу это, но это не основное в этом вопросе), я, наконец, заставлю медленные вещи работать параллельно. Но если второй вызов getValue("key1")
выполняется до завершения первого вызова, я в конечном итоге выполняю один и тот же медленный вызов дважды, и все работает медленнее, чем в старой версии, потому что она не использует кэш.<
Есть ли что-то вроде await("key1")
, которое будет ожидать, только если предыдущий вызов с "key1"
все еще ожидает?
EDIT (продолжение комментария)
Под "ускорить" я имею в виду более отзывчивый.
Например, когда пользователь выбирает материал в раскрывающемся списке, я хочу обновить список доступных толщин или цветов в других раскрывающихся списках и другие свойства материала в других элементах пользовательского интерфейса. Иногда это запускает каскад событий, требующих многократного использования одного и того же getValue("key")
.
Например, при изменении материала могут быть вызваны несколько функций: updateThicknesses()
, updateHoleOffsets()
, updateMaxWindLoad()
, updateMaxHoleDistances()
и т. д. Каждая функция считывает значения из элементов пользовательского интерфейса и решает, делать ли собственные медленные вычисления независимо от других функций. Каждой функции может потребоваться несколько вызовов http для вычисления некоторых параметров, а некоторые из этих параметров могут потребоваться нескольким функциям.
В старой реализации функции вызывались последовательно, поэтому вторая функция могла использовать некоторые значения, кэшированные при обработке первой. Пользователь увидит, как каждый раздел интерфейса обновляется последовательно в течение 5-6 секунд в первый раз и очень быстро в последующие разы, если только новое значение не требует некоторых новых вызовов конечной точки http.
.Новая асинхронная реализация вызывает все функции одновременно, поэтому каждая функция в конечном итоге вызывает одни и те же конечные точки http, поскольку их результаты еще не кэшированы.
"Я пытаюсь использовать асинхронный режим для его ускорения" Обычно асинхронный режим/ожидание используется, чтобы сделать приложение более отзывчивым или масштабируемым, а не более быстрым. Можете ли вы показать, как вы собираетесь использовать асинхронную версию метода
getValue
(getValueAsync
?), чтобы ускорить работу приложения?Загрузите бесплатную небольшую книгу Стивена Туба Асинхронный шаблон на основе задач. Там есть реализация AsyncCache.
Вас может заинтересовать что-то вроде AsyncLazy.
Простой метод заключается в кэшировании объектов задач вместо значений из них.
Вы можете использовать
ConcurrentDictionary<K,V>
с реализациейGetOrAddAsync
, которая принимает асинхронные делегаты (Func<TKey, Task<TValue>>
). Есть несколько реализаций этого метода здесь.