【发布时间】:2021-07-01 16:01:31
【问题描述】:
我正在重写一个旧应用程序,我正在尝试使用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;
}
如果我每次拨打getValueasync 和awaitgetValue,那么一切正常。但它并不比旧版本快,因为一切都像以前一样同步运行。
如果我删除await(好吧,如果我推迟它,但这不是这个问题的重点),我终于可以并行运行缓慢的东西。但是,如果在第一次调用完成之前对getValue("key1") 执行第二次调用,我最终会执行两次相同的慢速调用,并且一切都比旧版本慢,因为它没有利用缓存。
有没有像await("key1") 这样的东西,只有在之前与"key1" 的通话仍在等待时才会等待?
编辑(跟进评论)
“加快速度”是指响应速度更快。
例如,当用户在下拉列表中选择材料时,我想更新其他下拉列表中的可用厚度或颜色列表以及其他 UI 元素中的其他材料属性。有时这会触发一系列事件,需要多次使用相同的getValue("key")。
例如当材质发生变化时,可能会调用几个函数:updateThicknesses()、updateHoleOffsets()、updateMaxWindLoad()、updateMaxHoleDistances()等。每个函数从 UI 元素中读取值并决定是否执行它自己的慢速计算独立于其他函数。每个函数可能需要几次 http 调用来计算一些参数,而其中一些参数可能需要多个函数。
旧的实现是按顺序调用函数,所以第二个函数在处理第一个函数时会利用缓存的一些值。用户第一次会看到界面的每个部分在 5-6 秒内按顺序更新,接下来的时间会非常快,除非新值需要一些新的 http 端点调用。
新的异步实现同时调用所有函数,因此每个函数最终都会调用相同的 http 端点,因为它们的结果尚未缓存。
【问题讨论】:
-
"我正在尝试使用 async 来加速它" 通常 async/await 用于使应用程序更具响应性或可扩展性,而不是更快。您能否展示一下您打算如何使用异步版本的
getValue方法(getValueAsync?)来加快应用程序的速度? -
免费下载Task-based Asynchronous Pattern Stephen Toub 的小书。那里有一个 AsyncCache 实现。
-
您可能对AsyncLazy 之类的内容感兴趣。
-
一个简单的方法是缓存任务对象而不是它们的值。
-
您可以将
ConcurrentDictionary<K,V>与接受异步委托的GetOrAddAsync实现一起使用(Func<TKey, Task<TValue>>)。这个方法有一些实现here.
标签: c# design-patterns async-await