This is sort of a follow up question on https://stackoverflow.com/questions/754661/httpruntime-cache-best-practices/11431198 where a reply from frankadelic contains this quote and code sample from http://msdn.microsoft.com/en-us/magazine/cc500561.aspx:
The problem is that if you've got a query that takes 30 seconds and you're executing the page every second, in the time it takes to populate the cache item, 29 other requests will come in, all of which will attempt to populate the cache item with their own queries to the database. To solve this problem, you can add a thread lock to stop the other page executions from requesting the data from the database.
// check for cached results
object cachedResults = ctx.Cache["PersonList"];
ArrayList results = new ArrayList();
if (cachedResults == null)
{
// lock this section of the code
// while we populate the list
lock(lockObject)
{
// only populate if list was not populated by
// another thread while this thread was waiting
if (cachedResults == null)
{
...
}
}
}
In my opinion this code sample would have benefitted from showing the actual cache assign/get logic as well. My assumption also is that the cachedResults
reference in the sample above should be a static reference, otherwise different threads wont access the same instance.
Is this correct and would the following be a correct implementation of a property where you want to lazy-load data through the cache?
private static object _someDataCacheLock = new object();
private static object _cachedResults;
public List<object> SomeData
{
get
{
HttpContext ctx = HttpContext.Current;
_cachedResults = ctx.Cache["SomeData"] as List<object>;
if (_cachedResults == null)
{
// lock this section of the code
// while we populate the list
lock (_someDataCacheLock)
{
// only populate if list was not populated by
// another thread while this thread was waiting
if (_cachedResults== null)
{
_cachedResults= GetSomeData(); //db access inside GetSomeData()
ctx.Cache.Insert("SomeData",
_cachedResults,
null, DateTime.Now.AddMinutes(10),
TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
}
return _cachedResults;
}
}
Ok, so I'm going ahead with this code instead, where I've added one row that in my opinion must have been missing from the MSDN sample... Any feedback on this?
private static object _someDataCacheLock = new object();
public List<object> SomeData
{
get
{
HttpContext ctx = HttpContext.Current;
List<object> cachedResults = ctx.Cache["SomeData"] as List<object>;
if (cachedResults == null)
{
// lock this section of the code
// while we populate the list
lock (_someDataCacheLock)
{
//This row was missing in the MSDN sample, I believe...
cachedResults = ctx.Cache["SomeData"] as List<object>;
// only populate if list was not populated by
// another thread while this thread was waiting
if (cachedResults == null)
{
cachedResults = GetSomeData(); //db access inside GetSomeData()
ctx.Cache.Insert("SomeData",
cachedResults,
null, DateTime.Now.AddMinutes(10),
TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
}
return cachedResults;
}
}
_cachedResults
instead of_cachedResults
? And what is the type of the object,List<object>
orPageDataCollection
? \$\endgroup\$