Singletons

I was experimenting with C# 4.0’s parallel functions (specifically parallel.foreach) and I had problems with a database-file sharing permission exception last night. What’s funny is, that’s supposed to be impossible. The Database Server object was a singleton, which meant that you cannot instantiate more than one instance of the class.
Design Pattern: Singleton
Pattern Type: Creational
First Stated in: http://en.wikipedia.org/wiki/Design_Patterns_(book)
Description: Ensure a class has only one instance, and provide a global point of access to it.

I started with this code:
public static DataManager Instance 
{
get; private set;
}
static DataManager()
{
Instance = new DataManager();
}
private DataManager() { }

Fairy standard. Line 1 exposed the instance, lines 5-8 is an internal lazy initialization, and the last line prevented the consumer from initializing the class themselves. Can you guess how the program managed to get around this limitation? It turns out that my application is a bit too fast. I mentioned earlier that I used Parallel.ForEach to access the object: this means that every cycle of the for loop is done in parallel of each other. If I had at least two objects inside the ForEach, and there’s enough CPU and RAM remaining, they would both run at the same time- initializing the object twice.

Poking around the web for solutions, I found one that used a double-locking algorithm which (guess what?) locked the instance from other parallel threads.
private static volatile DataManager _Instance;
private static object syncLock = new object();

public static DataManager Instance
{
get
{
if (_Instance == null)
{
lock (syncLock)
{
if (_Instance == null)
_Instance = new DataManager();
}
}
return _Instance;
}
}

private DataManager() { }

Line-by-line: The first line uses a keyword that I haven’t seen since college: volatile. The keyword locks the the _Instance field until it is initialized and fully accessible. Next line (2) is a dummy field of type: object which we see again on line 10- basically, we’re avoiding a deadlock by making the object always accessible, which is only possible if we don't lock the type itself (DataManager) down. The other lines are self-explanatory: A lazy initialization and a constructor that blocks the consumer from initializing more objects.

I compiled the app and it looks like everything works as expected for now :) Until the next bug, ciao~!

0 comments:

Post a Comment