Context
In some situations, a certain type of
data needs to be available to all other objects in the application. In most
cases, this type of data is also unique in the system. For example, a user
interface can have only one mouse pointer that all applications must access.
Problem
How
do you make an instance of an object globally available and guarantee that only
one instance of the class is created?
Forces
The
following force act on a system within this context and must be reconciled as
you consider a solution to the problem:
- To ensure that only a single instance of a class can exist, you must control the instantiation process. This implies that you need to prevent other objects from creating an instance of the class by using the instantiation mechanism inherent in the programming language (for example, by using the new operator). The other part of controlling the instantiation is providing a central mechanism by which all objects can obtain a reference to the single instance.
Solution
Singleton provides a global, single instance by:
- Making the class create a single instance of itself.
- Allowing other objects to access this instance through a class method that returns a reference to the instance. A class method is globally accessible.
- Declaring the class constructor as private so that no other object can create a new instance.
Resulting Context
Singleton results in the following benefits:
·
Instance control. Singleton prevents other objects from instantiating their own
copies of the Singleton object,
ensuring that all objects access the single instance.
Examples:
Implementation Strategy:
Singleton
using System;
public class Singleton
{
private static
Singleton instance;
private Singleton() {}
public static
Singleton Instance
{
get
{
if (instance ==
null)
{
instance =
new Singleton();
}
return
instance;
}
}
}
This implementation has two main advantages:
- Because the instance is created inside the Instance property method, the class can exercise additional functionality (for example, instantiating a subclass), even though it may introduce unwelcome dependencies.
- The instantiation is not performed until an object asks for an instance; this approach is referred to as lazy instantiation. Lazy instantiation avoids instantiating unnecessary singletons when the application starts.
Let’s say we have a field of a type that is
expensive to construct
class Foo
{
public readonly
Expensive expensive = new Expensive();
...
}
The problem with this code is that instansiating
Foo incurs the performance cost of instansiating
Expensive - whether-or-not the Expensive field is ever accessed. The obvious answer is to construct the
instance on demand or lazily instansiate the field:
Expensive - whether-or-not the Expensive field is ever accessed. The obvious answer is to construct the
instance on demand or lazily instansiate the field:
class Foo
{
Expensive
_expensive;
public Expensive
{
get
{
if
(_expensive == null) _expensive = new Expensive();
return
_expensive;
}
}
...
}
The
main disadvantage of this implementation, however, is that it is not safe for
multithreaded environments. If separate threads of execution enter the Instance property method at the same time, more
that one instance of the Singleton object may be created. Each thread could
execute the following statement and decide that a new instance has to be
created:
if (instance == null)
Static Initialization
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton Instance
{
get
{
return instance;
}
}
}
In this strategy, the instance is
created the first time any member of the class is referenced. The common
language runtime takes care of the variable initialization. The class is marked sealed to
prevent derivation, which could add instances. In addition, the variable
is marked readonly, which means that it can be assigned only during
static initialization (which is shown here) or in a class constructor.
It addresses the two basic problems that
the Singleton pattern
is trying to solve: global access and instantiation control. The public static
property provides a global access point to the instance. Also, because the
constructor is private, the Singleton class
cannot be instantiated outside of the class itself; therefore, the variable
refers to the only instance that can exist in the system.
Because the Singleton instance
is referenced by a private static member variable, the instantiation does not
occur until the class is first referenced by a call to the Instance property.
This solution therefore implements a form of the lazy instantiation property.
Multithreaded
Singleton
The following implementation allows only
a single thread to enter the critical area, which the lock block
identifies, when no instance of Singleton has
yet been created:
using System; public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{ lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
}
This
approach ensures that only one instance is created and only when the instance
is needed. Also, the variable is declared to be volatileto
ensure that assignment to the instance variable completes before the instance
variable can be accessed. Lastly, this approach uses asyncRoot instance to lock on, rather than locking
on the type itself, to avoid deadlocks.
This
double-check locking approach solves the thread concurrency problems while
avoiding an exclusive lock in every call to the Instanceproperty
method. It also allows you to delay instantiation until the object is first
accessed. In practice, an application rarely requires this type of
implementation. In most cases, the static initialization approach is
sufficient.
No comments:
Post a Comment