이 코드에는 동시성 문제가 있다고 가정합니다.
const string CacheKey = "CacheKey";
static string GetCachedData()
{
string expensiveString =null;
if (MemoryCache.Default.Contains(CacheKey))
{
expensiveString = MemoryCache.Default[CacheKey] as string;
}
else
{
CacheItemPolicy cip = new CacheItemPolicy()
{
AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20))
};
expensiveString = SomeHeavyAndExpensiveCalculation();
MemoryCache.Default.Set(CacheKey, expensiveString, cip);
}
return expensiveString;
}
동시성 문제의 이유는 여러 스레드가 null 키를 얻은 다음 캐시에 데이터를 삽입하려고 할 수 있기 때문입니다.
이 코드 동시성 증명을 만드는 가장 짧고 깨끗한 방법은 무엇입니까? 캐시 관련 코드에서 좋은 패턴을 따르고 싶습니다. 온라인 기사에 대한 링크는 큰 도움이 될 것입니다.
최신 정보:
@Scott Chamberlain의 답변을 기반 으로이 코드를 생각해 냈습니다. 누구든지 이것으로 성능이나 동시성 문제를 찾을 수 있습니까? 이것이 작동하면 많은 코드 줄과 오류를 줄일 수 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Caching;
namespace CachePoc
{
class Program
{
static object everoneUseThisLockObject4CacheXYZ = new object();
const string CacheXYZ = "CacheXYZ";
static object everoneUseThisLockObject4CacheABC = new object();
const string CacheABC = "CacheABC";
static void Main(string[] args)
{
string xyzData = MemoryCacheHelper.GetCachedData<string>(CacheXYZ, everoneUseThisLockObject4CacheXYZ, 20, SomeHeavyAndExpensiveXYZCalculation);
string abcData = MemoryCacheHelper.GetCachedData<string>(CacheABC, everoneUseThisLockObject4CacheXYZ, 20, SomeHeavyAndExpensiveXYZCalculation);
}
private static string SomeHeavyAndExpensiveXYZCalculation() {return "Expensive";}
private static string SomeHeavyAndExpensiveABCCalculation() {return "Expensive";}
public static class MemoryCacheHelper
{
public static T GetCachedData<T>(string cacheKey, object cacheLock, int cacheTimePolicyMinutes, Func<T> GetData)
where T : class
{
//Returns null if the string does not exist, prevents a race condition where the cache invalidates between the contains check and the retreival.
T cachedData = MemoryCache.Default.Get(cacheKey, null) as T;
if (cachedData != null)
{
return cachedData;
}
lock (cacheLock)
{
//Check to see if anyone wrote to the cache while we where waiting our turn to write the new value.
cachedData = MemoryCache.Default.Get(cacheKey, null) as T;
if (cachedData != null)
{
return cachedData;
}
//The value still did not exist so we now write it in to the cache.
CacheItemPolicy cip = new CacheItemPolicy()
{
AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(cacheTimePolicyMinutes))
};
cachedData = GetData();
MemoryCache.Default.Set(cacheKey, cachedData, cip);
return cachedData;
}
}
}
}
}
Dictionary<string, object>
키가 당신이 사용하는 것과 같은 키 MemoryCache
이고 사전의 객체가 Object
당신이 잠그는 기본 일 뿐이라면 쉽게 할 수 있습니다 . 그러나 그 말을 듣고 Jon Hanna의 답변을 읽는 것이 좋습니다. 적절한 프로파일 링이 없으면 두 인스턴스를 SomeHeavyAndExpensiveCalculation()
실행하고 하나의 결과를 버리는 것보다 잠금을 사용하여 프로그램 속도를 더 느리게 할 수 있습니다 .
ReaderWriterLockSlim
않습니까?