ASP.NET:缓存任务并在同步代码中调用其结果

ASP.NET:缓存任务并在同步代码中调用其结果,asp.net,caching,async-await,task,Asp.net,Caching,Async Await,Task,我有同步的HttpHandler。我想缓存HttpClient.GetAsync的结果,并在我的HttpHandler中使用它。我是这样做的: public static class CacheFacade { private static Cache Cache => HttpRuntime.Cache; private const string CacheKey = "asynccache"; private static readonly object _lo

我有同步的HttpHandler。我想缓存HttpClient.GetAsync的结果,并在我的HttpHandler中使用它。我是这样做的:

public static class CacheFacade
{
    private static Cache Cache => HttpRuntime.Cache;
    private const string CacheKey = "asynccache";
    private static readonly object _lockObject = new object();

    public static string GetStringFromCache()
    {
        if (Cache[CacheKey] == null)
        {    
            lock(_lockObject)
            {   
                if (Cache[CacheKey] == null)
                {
                    InitCache();
                }
            }

            //fallback here; I can use data from some synchronous source
            return "init cache" + " - " + Thread.CurrentThread.ManagedThreadId;
        }
        var task = (Task<string>) Cache[CacheKey];

        if (!task.IsCompleted)
        {
            //and fallback here too
            return task.Status + " - " + DateTime.UtcNow + " - " + Thread.CurrentThread.ManagedThreadId;
        }

        return task.Result;
    }

    private static void InitCache()
    {
        var task = Task.Run(GetDataAsync);
        Cache.Insert(CacheKey, task, null, DateTime.Now.Add(TimeSpan.FromSeconds(10)),
            Cache.NoSlidingExpiration);
    }

    private static async Task<string> GetDataAsync()
    {
        using (var httpClient = new HttpClient())
        {
            await Task.Delay(TimeSpan.FromSeconds(2));
            var res = await httpClient.GetAsync("http://www.google.com");
            return res.StatusCode + " - " + DateTime.UtcNow + " - " + Thread.CurrentThread.ManagedThreadId;
        }
    }
}
公共静态类CacheFacade
{
私有静态缓存=>HttpRuntime.Cache;
private const string CacheKey=“asynccache”;
私有静态只读对象_lockObject=新对象();
公共静态字符串GetStringFromCache()
{
if(Cache[CacheKey]==null)
{    
锁定(锁定对象)
{   
if(Cache[CacheKey]==null)
{
InitCache();
}
}
//退一步;我可以使用来自某个同步源的数据
返回“初始化缓存”+“-”+Thread.CurrentThread.ManagedThreadId;
}
var task=(task)缓存[CacheKey];
如果(!task.IsCompleted)
{
//在这里也退后一步
返回task.Status+“-”+DateTime.UtcNow+“-”+Thread.CurrentThread.ManagedThreadId;
}
返回任务。结果;
}
私有静态void InitCache()
{
var task=task.Run(GetDataAsync);
Cache.Insert(CacheKey,task,null,DateTime.Now.Add(TimeSpan.FromSeconds(10)),
Cache.NoSlidingExpiration);
}
私有静态异步任务GetDataAsync()
{
使用(var httpClient=new httpClient())
{
等待任务延迟(时间跨度从秒(2));
var res=wait httpClient.GetAsync(“http://www.google.com");
返回res.StatusCode+“-”+DateTime.UtcNow+“-”+Thread.CurrentThread.ManagedThreadId;
}
}
}
它起作用了。这种方法有什么缺点吗?
更新:根据添加了带有双重检查的锁。

这没问题

请注意,这些缓存项只能存储在进程中。无法使用进程外缓存,因为无法序列化任务。你可能在乎,也可能不在乎

在您的
中,请在此后退;我可以使用一些同步源的数据
来处理任务,您也可以阻止任务(或者在架构上可能的情况下等待它)


HTTP处理程序也支持使用任务进行异步处理。我相信你需要一个小小的助手/包装器来完成那些易于编写或在网络上可用的东西。

有什么具体问题吗?是的,我知道它需要进程内模式。关于回退:在实际项目中,我有对数据库的同步调用。我知道我可以使用异步HttpHandler,但项目非常大,在许多地方引入异步工作流非常困难。我正在考虑从多个请求访问这个静态类。缓存是线程安全的,所以检查null和其他工作是否正常(我想)。但是:如果我想在静态变量中缓存数据,而不是缓存,那么需要为“if(cache[CacheKey]==null){…}”添加锁,对吗?您在这里只使用线程安全缓存,所以不必担心。对于静态变量,您确实需要同步。ConcurrentDict是一个简单而安全的解决方案,但它不支持逐出。