[C#]関数のキャッシュを容易にする方法
4203 ワード
構想
MemoryCacheを使用して「特定の関数の特定の入力」の実行結果をキャッシュすると、dbおよびredisへのアクセスを大幅に節約できます.
外部から関数の実行結果をキャッシュするのは,関数を修正して関数内部をキャッシュするよりも緩やかに結合し,侵入性がない.
インプリメンテーション
MemoryCacheを使用して「特定の関数の特定の入力」の実行結果をキャッシュすると、dbおよびredisへのアクセスを大幅に節約できます.
外部から関数の実行結果をキャッシュするのは,関数を修正して関数内部をキャッシュするよりも緩やかに結合し,侵入性がない.
インプリメンテーション
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WSQ.Common
{
///
/// MemoryCache 。 db redis , 。
/// :var preData = CacheHelper.WithCache("userstock_GetUSPreDataTradingCode_" + stockCode, ()=>Redis.QuoteData.GetUSPreDataTradingCode(stockCode), 5000);
///
public static class CacheHelper
{
private static MemoryCache mc = new MemoryCache(Guid.NewGuid().ToString());
///
/// MemoryCache getData 。Func ()=>getData(a,b,c)
///
///
/// key
///
///
///
///
public static T WithCache(string key, Func getData, double cacheMilli = 1000) where T : new()
{
object obj = mc.Get(key);
if (obj != null)
{
return (T)obj;
}
else
{
T dd = getData();
if (dd == null)
{
dd = new T();
}
mc.Set(key, dd, DateTime.Now.AddMilliseconds(cacheMilli));
return dd;
}
}
private class CacheInfo
{
public string key;
public DateTime lastCacheTime;
public double cacheMilli;
///
/// ,
///
public bool isRefreshing;
///
///
///
///
public bool NeedPredicateRefresh()
{
var milliExist = (DateTime.Now - this.lastCacheTime).TotalMilliseconds;
// 。
// 。
// 。
bool needRefresh = !isRefreshing && this.cacheMilli > 500 && milliExist > (this.cacheMilli * 0.8);
return needRefresh;
}
}
private static ConcurrentDictionary dicCacheInfo = new ConcurrentDictionary();
///
/// MemoryCache getData 。Func ()=>getData(a,b,c)。
/// , , Func 。
///
///
/// key
///
///
///
///
public static T WithCacheRefresh(string key, Func getData, double cacheMilli = 1000) where T : new()
{
var info = dicCacheInfo.GetOrAdd(key, new CacheInfo()
{
key = key,
lastCacheTime = new DateTime(),
cacheMilli = cacheMilli,
isRefreshing = false
});
object obj = mc.Get(key);
if (obj != null)
{
//cache ,
if (info.NeedPredicateRefresh())
{
// isRefreshing,
info.isRefreshing = true;
Thread th = new Thread(new ThreadStart(() => CheckRefreshCache(key, getData, cacheMilli)));
th.Name = "WithCacheConcurrent_refresh";
th.IsBackground = true;
th.Start();
}
return (T)obj;
}
else
{
T dd = getData();
if (dd == null)
{
dd = new T();
}
mc.Set(key, dd, DateTime.Now.AddMilliseconds(cacheMilli));
info.lastCacheTime = DateTime.Now;
info.cacheMilli = cacheMilli;
return dd;
}
}
///
///
///
///
///
///
///
private static void CheckRefreshCache(string key, Func getData, double cacheMilli) where T : new()
{
dicCacheInfo.TryGetValue(key, out CacheInfo info);
if (info == null)
{
return;
}
try
{
T dd = getData();
if (dd == null)
{
dd = new T();
}
mc.Set(key, dd, DateTime.Now.AddMilliseconds(cacheMilli));
info.lastCacheTime = DateTime.Now;
info.cacheMilli = cacheMilli;
}
catch (Exception)
{
}
info.isRefreshing = false;
}
}
}