"Tell us about a design pattern that you've used."
Probably one of the most asked questions in a programming interview. And the canned answer always happens to be "Singleton". Why? Because that's the easiest design pattern which you cannot go wrong with. Or is it?
If you don't remember what singleton code looks like, I don't blame you because I don't either. But after giving a little bit of thought, you might be able to come up with this:
Well, true it doesn't have all the bells and whistles like thread-safety and whatnot, but it's a good start. To be frank, I hate locks. It makes the code looking out of place. May be it's just me.
But I have to admit, thread syncing is crucial if you are serious about multi-threaded execution. And you might even want to use "double-checked locking" to favor performance. But what if we could get the same without using locks?
Notice that the above code makes use of how static type initialization works. Static type initialization is guaranteed to happen only once per AppDomian hence line 3 will be executed by the runtime only once, no matter how many threads asked for it.
So what's the caveat? Hmm, glad that you asked. Well, apparently you cannot guarantee when this initialization kicks off, so spawning of our singleton instance will not exactly be "lazy". In fact, it would even be initialized without the class being referred at all. Yikes! Can we fix it?
That's why we've slapped a static constructor in line 6. Mind you, you could've written the same code without it and it'll still work. But with that in place, the compiler generates IL code which fires the initializers in a more predictable fashion. Now the initialization would only happen whenever you refer the static class for the first time. But still, it's not ideal. At least in theory.
What if you have other static members in this class? They could get referred elsewhere and your singleton instance would be spawned prematurely. It's a valid case hypothetically. To circumvent this, you could add a nested static class only to hold your singleton instance and return it when needed. But that's overkill in my opinion. I'm pretty content with the above.
Ok, but can't we achieve this lazy behavior with something much more simple? Sure you can. Lazy<T> to the rescue!
This seems to be the most elegant solution of all. It has everything we tried to achieve: performance and laziness in one package. Have you coded your singletons like this? I have to confess that I have not. In fact, I've only used the style shown in the 2nd code snippet. But I'm looking forward to try out the Lazy<T> implementation when I get my next chance. probably you should give it a shot too. Cheers!
PS: One more thing to note before wrapping up. By using locks or static initialization, you are only making your "singleton instance initialization" thread-safe. It doesn't magically make your other instance methods that do the real productive work thread-safe. You'll need to handle those case by case, if they are prone to be problematic in multi-threaded environments.
Post a Comment