The singleton pattern is one of the best-known patterns in
software engineering. Essentially, a singleton is a class which only allows a
single instance of itself to be created, and usually gives simple access to
that instance. Most commonly, singletons don't allow any parameters to be
specified when creating the instance - as otherwise a second request for an
instance but with a different parameter could be problematic! (If the same
instance should be accessed for all requests with the same parameter, the factory
pattern is more appropriate.) This article deals only with the situation where
no parameters are required. Typically a requirement of singletons is that they
are created lazily - i.e. that the instance isn't created until it is first
needed.
There are various different ways of implementing the
singleton pattern in C#. I shall present them here in reverse order of
elegance, starting with the most commonly seen, which is not thread-safe, and
working up to a fully lazily-loaded, thread-safe, simple and highly performant
version.
All these implementations share four
common characteristics, however:
- A single constructor, which is private and
parameterless. This prevents other classes from instantiating it (which
would be a violation of the pattern). Note that it also prevents
subclassing - if a singleton can be subclassed once, it can be subclassed
twice, and if each of those subclasses can create an instance, the pattern
is violated. The factory pattern can be used if you need a single instance
of a base type, but the exact type isn't known until runtime.
- The class is sealed. This is unnecessary, strictly
speaking, due to the above point, but may help the JIT to optimise things
more.
- A static variable which holds a reference to the single
created instance, if any.
- A public static means of getting the reference to the
single created instance, creating one if necessary.
Note that all of these
implementations also use a public static property Instance as the means of accessing the instance. In all cases, the
property could easily be converted to a method, with no impact on thread-safety
or performance.
First version - not thread-safe
// Bad code! Do not use!
public sealed class Singleton
{
private static Singleton instance=null;
private Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
public sealed class Singleton
{
private static Singleton instance=null;
private Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
As hinted at before, the above is not thread-safe. Two
different threads could both have evaluated the test
if (instance==null) and found it to be true, then
both create instances, which violates the singleton pattern. Note that in fact
the instance may already have been created before the expression is evaluated,
but the memory model doesn't guarantee that the new value of instance will be
seen by other threads unless suitable memory barriers have been passed.
Second version - simple thread-safety
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
This implementation is thread-safe. The thread takes out a lock on a shared
object, and then checks whether or not the instance has been created before
creating the instance. This takes care of the memory barrier issue (as locking
makes sure that all reads occur logically after the lock acquire, and unlocking
makes sure that all writes occur logically before the lock release) and ensures
that only one thread will create an instance (as only one thread can be in that
part of the code at a time - by the time the second thread enters it,the first
thread will have created the instance, so the expression will evaluate to
false). Unfortunately, performance suffers as a lock is acquired every time the
instance is requested. {
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
Note that instead of locking on
typeof(Singleton)
as some versions of this implementation do, I lock on the value of a static
variable which is private to the class. Locking on objects which other classes
can access and lock on (such as the type) risks performance issues and even
deadlocks. This is a general style preference of mine - wherever possible, only
lock on objects specifically created for the purpose of locking, or which
document that they are to be locked on for specific purposes (e.g. for
waiting/pulsing a queue). Usually such objects should be private to the class
they are used in. This helps to make writing thread-safe applications
significantly easier.
Fourth version - not quite as lazy, but thread-safe without
using locks
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}
private Singleton()
{
}
public static Singleton Instance
{
get
{
return instance;
}
}
}
As you can see, this is really is extremely simple - but why is it
thread-safe and how lazy is it? Well, static constructors in C# are specified
to execute only when an instance of the class is created or a static member is
referenced, and to execute only once per AppDomain. Given that this check for
the type being newly constructed needs to be executed whatever else happens, it
will be faster than adding extra checking as in the previous examples. There
are a couple of wrinkles, however: {
private static readonly Singleton instance = new Singleton();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}
private Singleton()
{
}
public static Singleton Instance
{
get
{
return instance;
}
}
}
- It's not as
lazy as the other implementations. In particular, if you have static
members other than
Instance, the first reference to those members will involve creating the instance. This is corrected in the next implementation. - There are
complications if one static constructor invokes another which invokes the
first again. Look in the .NET specifications (currently section 9.5.3 of
partition II) for more details about the exact nature of type initializers
- they're unlikely to bite you, but it's worth being aware of the
consequences of static constructors which refer to each other in a cycle.
- The
laziness of type initializers is only guaranteed by .NET when the type
isn't marked with a special flag called
beforefieldinit. Unfortunately, the C# compiler (as provided in the .NET 1.1 runtime, at least) marks all types which don't have a static constructor (i.e. a block which looks like a constructor but is marked static) asbeforefieldinit. I now have an article with more details about this issue. Also note that it affects performance, as discussed near the bottom of the page.
instance a public
static readonly variable, and get rid of the property entirely. This makes the
basic skeleton code absolutely tiny! Many people, however, prefer to have a
property in case further action is needed in future, and JIT inlining is likely
to make the performance identical. (Note that the static constructor itself is
still required if you require laziness.)
Fifth version - fully lazy instantiation
public sealed class Singleton{
private Singleton()
{
}
public static Singleton Instance { get { return Nested.instance; } }
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
Here, instantiation is triggered by the first reference to
the static member of the nested class, which only occurs in
Instance. This means the
implementation is fully lazy, but has all the performance benefits of the
previous ones. Note that although nested classes have access to the enclosing
class's private members, the reverse is not true, hence the need for instance to be internal here.
That doesn't raise any other problems, though, as the class itself is private.
The code is a bit more complicated in order to make the instantiation lazy,
however.
Sixth version - using .NET 4's Lazy<T>
type
If you're using .NET 4 (or higher), you can use the System.Lazy<T>
type to make the laziness really simple. All you need to do is pass a delegate
to the constructor which calls the Singleton constructor - which is done most
easily with a lambda expression.
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance { get { return lazy.Value; } }
private Singleton()
{
}
}
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance { get { return lazy.Value; } }
private Singleton()
{
}
}
No comments:
Post a Comment