如果我大多数时间都将结果缓存,请避免异步/等待

时间:2019-02-18 15:56:31

标签: c# async-await

在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()

任何见解将不胜感激。

4 个答案:

答案 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

Source

答案 3 :(得分:-2)

首先,这需要链接速度专家:

https://ericlippert.com/2012/12/17/performance-rant/

此类微优化通常留给JiT。我的经验法则是,如果您真的需要这种差异,那么您很可能在处理实时编程。对于实时警告,像.NET这样的垃圾收集运行时很可能是错误的环境。直接内存管理之类的东西,例如不安全的代码-甚至本机C ++或汇编程序-都会更好。

第二,这里的任务可能只是错误的工具。也许您真正想要的是类似Lazy[T]的东西?还是5个不同的Chache类中的任何一个? (与计时器一样,特定的用户界面技术大约有一种。)

可以将任何工具用于多种用途。但是任务是用于多任务的,并且有更好的缓存和延迟初始化工具。而且Lazy [T]本质上就是线程保存。