Wednesday, July 9, 2008

[2008.07.09] Coordinate Data Structures in System.Threading [Part III/III]

[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
Example: using properties to model lazy initialization

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
Example: with LazyInit<T> the previous example can be written as:

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
Example: using LazyInit<T> where T has a public, parameterless constructor:

// 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
Example: the CreateMyData function is used to create and return an instance of MyData when requested to do so by LazyInit<T>

// 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
Example: the mode is passed as a second parameter to the constructor of LazyInit<T>

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: