在C#中使用缓存时,其中哪些是最佳选择?
我对编译器级别感兴趣,这是最优雅/性能最高的解决方案。
例如,.net编译器是否使用任何技巧来知道何时代码将同步运行,并避免创建/运行不必要的异步等待代码?
选项1,使用org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in ServletContext resource [/WEB-INF/spring/appServlet/servlet-context.xml]: Cannot resolve reference to bean 'dataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in ServletContext resource [/WEB-INF/spring/appServlet/servlet-context.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'url' threw exception; nested exception is java.lang.IllegalArgumentException: Property 'url' must not be empty
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:336)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1457)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1198)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:624)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:672)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:543)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1144)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1091)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5003)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5317)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1423)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1413)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in ServletContext resource [/WEB-INF/spring/appServlet/servlet-context.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'url' threw exception; nested exception is java.lang.IllegalArgumentException: Property 'url' must not be empty
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1494)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1198)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
... 31 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'url' threw exception; nested exception is java.lang.IllegalArgumentException: Property 'url' must not be empty
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1490)
... 39 more
Feb 18, 2019 9:19:32 PM org.apache.catalina.core.StandardContext loadOnStartup
SEVERE: Servlet [appServlet] in web application [/heritage-api] threw load() exception
org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessException details (1) are:
PropertyAccessException 1:
org.springframework.beans.MethodInvocationException: Property 'url' threw exception; nested exception is java.lang.IllegalArgumentException: Property 'url' must not be empty
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1171)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:923)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:82)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1490)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1198)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1457)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1198)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:624)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:672)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:543)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1144)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1091)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5003)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5317)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1423)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1413)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: Property 'url' must not be empty
at org.springframework.util.Assert.hasText(Assert.java:162)
at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.setUrl(AbstractDriverBasedDataSource.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1158)
... 43 more
并使用async/await
缓存值;
Task.FromResult
选项2,避免 public async Task<T> GetValue<T>(string key)
{
if (_cache.containsKey(key))
{
// 99% of the time will hit this
return Task.FromResult(_cache.GetItem(key));
}
return await _api.GetValue(key);
}
并使用async/await
之类的东西几次,API端点都会被命中。
GetAwaiter().GetResult()
任何见解将不胜感激。
答案 0 :(得分:4)
官方的方法是缓存Task<T>
,而不是T
。
这还具有以下优点:如果有人请求该值,则可以启动请求以获取该值,然后缓存生成的进行中的Task<T>
。如果其他人在请求完成之前请求了缓存的值,则还会向他们提供相同的进行中的Task<T>
,并且您最终不会发出两个请求。
例如:
public Task<T> GetValue<T>(string key)
{
// Prefer a TryGet pattern if you can, to halve the number of lookups
if (_cache.containsKey(key))
{
return _cache.GetItem(key);
}
var task = _api.GetValue(key);
_cache.Add(key, task);
return task;
}
请注意,在这种情况下,您需要考虑失败:如果对API的请求失败,则将缓存包含异常的Task
。这可能是您想要的,但可能不是。
如果由于某些原因您无法执行此操作,那么官方建议是将ValueTask<T>
用于高性能方案。此类型有一些陷阱(例如您无法等待两次),所以我建议使用reading this。如果您对性能的要求不高,可以使用Task.FromResult
。
答案 1 :(得分:3)
您的第一个无效。最简单,而且大多数时间都可以使用:
public async Task<T> GetValueAsync<T>(string key)
{
if (_cache.ContainsKey(key))
{
return _cache.GetItem(key);
}
T result = await _api.GetValueAysnc(key);
_cache.Add(key, result);
return result;
}
或者如果可能更好:
public async Task<T> GetValueAsync<T>(string key)
{
if (_cache.TryGet(key, out T result))
{
return result;
}
result = await _api.GetValueAysnc(key);
_cache.Add(key, result);
return result;
}
这很好用,当值在缓存中时将返回一个已经完成的任务,因此await
添加将立即继续。
但是如果大部分时间该值在高速缓存中 ,并且该方法经常被调用,足以使async
周围的额外设备产生影响那么您可以在这种情况下完全避免它:
public Task<T> GetValueAsync<T>(string key)
{
if (_cache.TryGet(key, out Task<T> result))
{
return result;
}
return GetAndCacheValueAsync(string key);
}
private async Task<T> GetAndCacheValueAsync<T>(string key)
{
var task = _api.GetValueAysnc(key);
result = await task;
_cache.Add(key, task);
return result;
}
如果缓存了值,我们将避免async
周围的状态机,也避免创建新的Task<T>
,因为我们已经存储了实际的Task
。每个步骤仅在第一种情况下完成。
答案 2 :(得分:1)
您正在寻找的可能是memoization。
实现可能是这样的:
public static Func<T, TResult> Memoize<T, TResult>(this Func<T, TResult> f)
{
var cache = new ConcurrentDictionary<T, TResult>();
return a => cache.GetOrAdd(a, f);
}
Measure(() => slowSquare(2)); // 00:00:00.1009680
Measure(() => slowSquare(2)); // 00:00:00.1006473
Measure(() => slowSquare(2)); // 00:00:00.1006373
var memoizedSlow = slowSquare.Memoize();
Measure(() => memoizedSlow(2)); // 00:00:00.1070149
Measure(() => memoizedSlow(2)); // 00:00:00.0005227
Measure(() => memoizedSlow(2)); // 00:00:00.0004159
答案 3 :(得分:-2)
首先,这需要链接速度专家:
https://ericlippert.com/2012/12/17/performance-rant/
此类微优化通常留给JiT。我的经验法则是,如果您真的需要这种差异,那么您很可能在处理实时编程。对于实时警告,像.NET这样的垃圾收集运行时很可能是错误的环境。直接内存管理之类的东西,例如不安全的代码-甚至本机C ++或汇编程序-都会更好。
第二,这里的任务可能只是错误的工具。也许您真正想要的是类似Lazy[T]的东西?还是5个不同的Chache类中的任何一个? (与计时器一样,特定的用户界面技术大约有一种。)
可以将任何工具用于多种用途。但是任务是用于多任务的,并且有更好的缓存和延迟初始化工具。而且Lazy [T]本质上就是线程保存。