Wednesday, May 9, 2007

Singleton: Ammendment 1

An excellent issue was raised by Farzad in his comment to my previous post (Singleton) regarding the fact that when you have a multi-instanced singleton and the singleton is going to decide based on the current thread what object to return it's better to store it in the thread using SetData & GetData.
I absolutely agree with Farzad since this will save us from the hassel of clean up issues. Consider the example of the multi-instanced connection manager:

public class ConnectionManager
{
/* ConnectionManager implementation */

//Singleton logic:
private ConnectionManager() { }

private static Dictionary _instances = new Dictionary();

public static ConnectionManager Instance
{
get
{
lock (_instances)
{
if (_instances.ContainsKey(System.Threading.Thread.CurrentThread) == false)
_instances.Add(System.Threading.Thread.CurrentThread,new ConnectionManager());
}
return _instances[System.Threading.Thread.CurrentThread];
}
}
}


One problem with the above code that you would have to resolve is what happens when a thread is terminated? The singleton will still contain a reference to the ConnectionManager associated with the thread and over time this could cause a memory leak. Now in my previous post this was only supposed to be an example, but in real life you should take this stuff into account.
There are multiple ways to solve this. In some systems you wouldn't have a problem because a very limited number of threads will be created and maintained throughout the lifetime of the system. You could also use weak references that will automatically get cleaned up when the garbage collector is doing a round of clean up.
But a very elegant solution would be using the Thread.SetData & GetData methods. So the result would look like this:

public class ConnectionManager
{
/* ConnectionManager implementation */

//Singleton logic:
private ConnectionManager() { }

public static ConnectionManager Instance
{
get
{
if (Thread.GetData(Thread.GetNamedDataSlot("ConnectionManager")) == null)
Thread.SetData(Thread.GetNamedDataSlot("ConnectionManager"), new ConnectionManager());

return (ConnectionManager)System.Threading.Thread.GetData(Thread.GetNamedDataSlot("ConnectionManager"));
}
}
}

As can be seen in the above code we aren't using an explicit data structure to store our ConnectionManager objects but we are using a place that the thread will provide for storing data specific to this thread. The other interesting bit about the above code is there is no need for locking due to the fact that the SetData & GetData function are operating on the current thread's data and there is no way two threads can access one thread's data at the same time :)

1 comment:

Jamal Mavadat said...

G'day dear Ehsan, :)

Last night, I just posted a comment in regards to Farzad's idea of binding conneciton-manager stuff to the CLR's logical threads: Read More...

Well, database connections are probably among the most expensive resources, indeed; therefore, require careful management and clean-up. I donot recommend using Thread Local Storage (TLS) like this, as this solution has a drawback of weak resource management.

What happens when a thread is terminated or aborted?! Simply the references to the locally stored items are released even if they're referring to some disposable objects which are to be disposed manually - by using "using" C# keyword for instance. You've just POSTPONED the real clean-up to the next time GC collects - please notice that resource clean-up should never be postponed. GC cannot be relied on, properly detecting the right collection time as she has no idea of such unmanaged resources. However, if you maintain the state of the connection manually and ensure it's closed after database transactions finalised, then SqlConnection disposal is not that much critical since most of the underlying resources have already been released. Object references stored through TLS, are either normal object references such as a reference to an instance of SqlConnection, or references to boxed-values as in the storage of a n integral value. In both cases, CLR does not take care of objects implementing IDisposable interface.

Speaking of weak references, I believe they are not of help in this scenario 'cause they make GC treat objects as garbage and this behaviour is not of great interest here. ConnectionManager keeps its connections inside TLS, accessible within the lifetime of the thread, and meanwhile GC is allowed to collect anytime, provided that you haven't upgraded the connection reference to a strong references. In other words, via this solution, weak references bring software defeats when the presence of the connection is expected but was already collected by GC, or become DISABLED by the programmer when a strong connection instance reference is stored within local scope.

Hope it clarifies,

Dear Ehsan, I'll be giving you a phone call or email by tomorrow for some personnal stuff as said on the phone before. See ya then, matey...

Cheers,
Jamal