/// <summary> /// Delegate to a method that returns a new instance of the object /// if to be used if its not found in the Cache. /// </summary> public delegate object CacheLoaderDelegate(); /// <summary> /// Return a typed object from Cache. This method locks the creation of a /// new instance to provide thread safety. /// </summary> /// <typeparam name="T">Type of object to return.</typeparam> /// <param name="key">The key used to reference the object.</param> /// <param name="expirationMinutes">The interval between the time the /// inserted object is last accessed and the time at which that object expires. /// If this value is the equivalent of 20 minutes, the object will expire and /// be removed from the cache 20 minutes after it was last accessed.</param> /// <param name="loaderDelegate">Delegate to a method that returns a new /// instance of the object if to be used if its not found in the Cache.</param> /// <returns>Instance of T</returns> public static T GetObject<T>(string key, int expirationMinutes, CacheLoaderDelegate loaderDelegate) { Cache webCache = HttpRuntime.Cache; object value = webCache[key]; // If the value is null create a new instance and add it to the cache. if (value == null) { // The reason for the lock and double null check is explained in this article // http://aspalliance.com/1705_A_New_Approach_to_HttpRuntimeCache_Management.all // It shows a coding flaw where multiple threads are checking the cache // concurrently. Each of them discovers the object missing from cache and // attempts to create and insert it. Because thread 1 has not inserted the // object before thread 2 checks for it, they both believe it’s missing and both // create it. The solution is to use a mutex that prevents multiple threads from // simultaneously entering the section of code that populates the cache. lock (typeof(CacheUtils)) { value = webCache[key]; if (value == null) { // Call loader delegate to a get a new instance. value = loaderDelegate(); // Insert the new object in the Cache for a sliding number of minutes. // This means if the ConfigurationManager is not accessed in that time period // it will be removed from cache. Also see notes in the class comments. webCache.Insert(key, value, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(expirationMinutes)); } } } // Verify that value (whether from the cache or the delegate is of the requested type. if (!(value is T)) { string message = string.Format("The value in the cache or returned by the loader delegate does not match the requested type: {0}", typeof(T)); throw new InvalidCastException(message); } // Return the requested type. return (T)value; }
Tuesday, March 31, 2009
Generic thread-safe access to Cache
In my last post I showed how we needed to move the log4net logger to Cache and use a lock to avoid multiple loads of the config file. Well we had some other places in the code that also stored objects in Cache so I wanted to repeat the same pattern. Using a generic method and a delegate I was able to create the following function that can safely access the Cache from multiple threads:
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment