Multithreaded removal of HttpRuntime.Cache items upon expiration

993 views Asked by At

Even if I use lock with static locker object to lock my code from accessing the same item at the same time, how can I guarantee that this item is not removed by HttpRuntime from another thread during my lock ? I don't see any SyncRoot property in HttpRuntime or HttpRuntime.Cache.

If I specify expiration callback and lock my static locker object inside that callback, would it be proper to do that ?

Is it bad to lock HttpRuntime.Cache expiration thread inside callback ?

2

There are 2 answers

2
Jon Hanna On BEST ANSWER

Looking for a "SyncRoot" property suggests that you use the SyncRoot property when using locking in combination with a collection. There's a reason why, although it is supported by later collections for backwards-compatibility, it tends to be hidden through implementing the interface explicitly. Really, the SyncRoot idea wasn't a very good one.

In this case, you're talking about a thread-safe collection, so there's even less need. System.Web.Caching.Cache does its own locking (or other mechanisms, it's specified to be threadsafe rather than to be threadsafe through a particular approach), so adding, accessing and removing an item from the collection across several threads won't corrupt it.

The only remaining risk, is if either the object itself isn't threadsafe, or if you access it from the collection several times in the same thread.

The second is easily avoided by not doing so. If you do:

(HttpRuntime.Cache.Get(someKey) as SomeType).SomeMethod();
(HttpRuntime.Cache.Get(someKey) as SomeType).SomeOtherMethod();

This only makes sense if you want to possibly be dealing with a different object in the second call than the first. Otherwise, it's easily resolved:

SomeType obj = HttpRuntime.Cache.Get(someKey) as SomeType;
obj.SomeMethod();
obj.SomeOtherMethod();

(You also get a slight performance improvement by not calling the Get method repeatedly.

If you need to worry about different threads calling SomeMethod() and SomeOtherMethod() at the same time you need to either ensure that those methods are threadsafe, or lock on an object that relates to the object in question. Most of the time the most obvious choice for an object that relates to an object, is that object itself. Hence:

SomeType obj = HttpRuntime.Cache.Get(someKey) as SomeType;
lock(obj)
{
  obj.SomeMethod();
  obj.SomeOtherMethod();
}

(Note that even if SomeMethod() and SomeOtherMethod() were threadsafe, we might still need to do this if the combination of them wasn't thread-safe. E.g. if the first reported on the objects state and we decided on the basis of that whether or not to perform the second, then we'd normally need to lock around that to prevent the state changing between the first and the second method call).

Of course, all other operations on the object would need to be locked in the same manner. It gets more complicated if we've multiple objects we need to synchronise as a unit. Then we need a more complicated rule about what object we lock on that simply locking on the object in question; because there's no single "object in question".

1
Peter Ritchie On

The cache is a private cache for your application. The only time the runtime does anything to it is when the application is restarted (i.e. it clears the cache). When you add an item to the cache, you can provide a callback to be informed of removals if you really want to be sure nothing else is messing with your cache.

I think it would be better to design the application and what is in the cache in such a ways as to minimize the need for two things to be accessing the same item in the cache though. Having a need for two threads to be accessing the same item in the cache suggests there's a design issue.