[6] System.Threading.LazyInit<T>
- provides support for several common patterns of thread-safe lazy initialization
- lazy initialization is a commonly-used tactic for delaying data initialization until the data is actually needed
- LazyInit<T> can be used to lazily initialize an expensive data structure especially in situations where as a developer you do not want to figure out if it has already been created or deal with locking for multithreaded access
- in single-threaded applications, this frequently takes a form similar to using properties
private MyData _data;
public MyData Data
{
get
{
// check is what you want exists
if (_data == null)
// if not, create it when needed
_data = new MyData();
// now have a lazily initialized
// data structure
return _data;
}
}
- however, multithreaded applications, where multiple threads may be accessing the lazily-initialized data simultaneously, need sophisticated, thread-safe constructs
private LazyInit<MyData> _data;
public MyData Data
{
get
{ return _data.Value; }
}
- there are several modes in which LazyInit<T> can operate:
[1] if the type of data being lazily initialized has a public, parameterless constructor, LazyInit<T> can use that constructor to initialize the instance of the type being passed into LazyInit
// create an instance of LazyInit<T> where
// T has a public, parameterless constructor
LazyInit<MyType> _myType; // this is all that is needed
// to get back the lazily initialized instance of the
// MyType that was created, call the Value
// property like:
_myType.Value;
[2] if the type doesn’t provide a public, parameterless constructor, or if different logic is needed, a delegate can be provided to LazyInit<T> that contains the initialization logic
// use a lambda to pass in the method that will
// create the type
private LazyInit<MyData> _data =
new LazyInit<MyData>(() => CreateMyData());
// get the type when needed
public MyData Data
{
get
{ return _data.Value; }
}
- given that you are working in a multithreaded or multi processor environment, then it is possible that multiple threads will race to create the lazily-initialized instance
- even though many instances may be created, only one instance will ever be published through LazyInit<T>.Value property
- LazyInit<MyData> provides an additional constructor that accepts a System.Threading.LazyInitMode which configures this behavior
private LazyInit<MyData> _data =
new LazyInit<MyData>(
() => CreateMyData(),
LazyInitMode.XXX);
- XXX can take on three values:
- AllowMultipleExecution
- this is the default value that will be used if this version of the LazyInit<T> constructor is used
- this means that even though multiple threads may race to initialize the value only one value will be published for all threads to access
- EnsureSingleExecution
- the initialization function will only be executed once so only one instance will ever be created, even if multiple threads race to initialize the value
- this value will be published for all threads to access
- ThreadLocal
- each thread will get its own published value
- basically, when _data.Value is called, you will get an instance of MyData because the CreateMyData method will be called to do the initialization
- the type is very small with little overhead
- if it is defined like LazyInit<MyData> _mt; then you are not doing any allocations at all until it is actually invoked and the cost of using this in a class is very negligible
[7] System.Threading.WriteOnce<T>
- WriteOnce<T> similar to a readonly construct, but inverted
- a readonly field is one which you can set once in a constructor and outside the constructor you can only read and not modify it
- a WriteOnce<T> is a single-assignment variable is a variable that can be written to only once and these types of variables are more relevant in concurent applications
- the WriteOnce<T>.Value property has get and set accessors; however, Value may only be retrieved after it has been set, and it may only be set once
- any accesses that violate these rules throw exceptions
- attempting to access the Value of a WriteOnce<T> before it’s been set is a significant programming error and as a result, such an action will invalidate the WriteOnce<T> instance for all future accesses
No comments:
Post a Comment