Export (0) Print
Expand All

Lazy<T> Constructor (Func<T>, LazyThreadSafetyMode)

Initializes a new instance of the Lazy<T> class that uses the specified initialization function and thread-safety mode.

Namespace:  System
Assembly:  mscorlib (in mscorlib.dll)

public Lazy(
	Func<T> valueFactory,
	LazyThreadSafetyMode mode
)

Parameters

valueFactory
Type: System.Func<T>

The delegate that is invoked to produce the lazily initialized value when it is needed.

mode
Type: System.Threading.LazyThreadSafetyMode

One of the enumeration values that specifies the thread safety mode.

ExceptionCondition
ArgumentOutOfRangeException

mode contains an invalid value.

ArgumentNullException

valueFactory is null.

The thread safety mode of a Lazy<T> instance describes the behavior when multiple threads try to initialize the Lazy<T> instance.

Exceptions that are thrown by valueFactory are cached, unless mode is PublicationOnly. For more information, see the Lazy<T> class or the System.Threading.LazyThreadSafetyMode enumeration.

The following example demonstrates the use of this constructor to create a lazy initializer that enables multiple threads to race to create an object lazily. Multiple threads might succeed in creating instances, but all threads use the instance that was created first. In addition, the example demonstrates that exceptions are never cached when you specify PublicationOnly, even if initialization is performed by a function instead of by the default constructor of the lazily created type.

NoteNote

For an example that demonstrates how to use this constructor in single-threaded scenarios (specifying LazyThreadSafetyMode.None for mode), see the Lazy<T>(Boolean) constructor. For an example that demonstrates how to use this constructor to provide locking instead of race conditions in multithreaded scenarios (specifying LazyThreadSafetyMode.ExecutionAndPublication for mode), see the Lazy<T>() constructor.

The example defines a LargeObject class that will be initialized lazily by any of several threads. The four key sections of code illustrate the creation of the initializer, the actual initialization, the initialization function, and the constructor and finalizer of the LargeObject class. At the beginning of the Main method, the example creates the Lazy<T> object that performs lazy initialization of the LargeObject:

lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, 
                             LazyThreadSafetyMode.PublicationOnly);

The lazy initializer uses a function to perform the initialization. In this case, a function is required because there is no default constructor for the LargeObject class.

The example creates and starts three threads that block on a ManualResetEvent object, so that the example can release the threads all at once. In the ThreadProc method that's used by all three threads, calling the Value property creates the LargeObject instance:

LargeObject large = null;
try
{
    large = lazyLargeObject.Value;

    // The following line introduces an artificial delay, to exaggerate the race  
    // condition.
    Thread.Sleep(5); 

    // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the   
    //            object after creation. You must lock the object before accessing it, 
    //            unless the type is thread safe. (LargeObject is not thread safe.) 
    lock(large)
    {
        large.Data[0] = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("LargeObject was initialized by thread {0}; last used by thread {1}.", 
            large.InitializedBy, large.Data[0]);
    }
}
catch (ApplicationException ex)
{
    Console.WriteLine("ApplicationException: {0}", ex.Message);
}

In the third key section of code, the lazy initialization function is called to create the LargeObject instance. The function throws an exception the first time it's called:

static int instanceCount = 0;
static LargeObject InitLargeObject()
{
    if (1 == Interlocked.Increment(ref instanceCount))
    {
        throw new ApplicationException(
            String.Format("Lazy initialization function failed on thread {0}.",
            Thread.CurrentThread.ManagedThreadId));
    }
    return new LargeObject(Thread.CurrentThread.ManagedThreadId);
}

With any other LazyThreadSafetyMode setting, an unhandled exception in the initialization function would be cached. However, PublicationOnly suppresses exception caching. The output from the example demonstrates that a subsequent attempt to initialize the object succeeds.

NoteNote

The exception message usually appears after messages indicating that other threads have successfully initialized the object. This is because of the delay introduced by throwing and catching the exception.

Because the constructor for the Lazy<T> instance specified LazyThreadSafetyMode.PublicationOnly, all three threads are allowed to create LargeObject instances. The example demonstrates this by displaying console messages in the constructor and in the finalizer of the LargeObject class:

public LargeObject(int initializedBy) 
{ 
    initBy = initializedBy;
    Console.WriteLine("Constructor: Instance initializing on thread {0}", initBy);
}

~LargeObject()
{
    Console.WriteLine("Finalizer: Instance was initialized on {0}", initBy);
}

The Lazy<T> object ensures that only one instance is used by all threads (except the thread where the initialization function throws an exception). The output from the example shows this.

NoteNote

For simplicity, this example uses a global instance of Lazy<T>, and all the methods are static (Shared in Visual Basic). These are not requirements for the use of lazy initialization.

using System;
using System.Threading;

class Program
{
    static Lazy<LargeObject> lazyLargeObject = null;

    // Factory function for lazy initialization. 
    static int instanceCount = 0;
    static LargeObject InitLargeObject()
    {
        if (1 == Interlocked.Increment(ref instanceCount))
        {
            throw new ApplicationException(
                String.Format("Lazy initialization function failed on thread {0}.",
                Thread.CurrentThread.ManagedThreadId));
        }
        return new LargeObject(Thread.CurrentThread.ManagedThreadId);
    }

    static void Main()
    {
        // The lazy initializer is created here. LargeObject is not created until the  
        // ThreadProc method executes.
        lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, 
                                     LazyThreadSafetyMode.PublicationOnly);


        // Create and start 3 threads, passing the same blocking event to all of them.
        ManualResetEvent startingGate = new ManualResetEvent(false);
        Thread[] threads = { new Thread(ThreadProc), new Thread(ThreadProc), new Thread(ThreadProc) };
        foreach (Thread t in threads)
        {
            t.Start(startingGate);
        }

        // Give all 3 threads time to start and wait, then release them all at once.
        Thread.Sleep(50);
        startingGate.Set();

        // Wait for all 3 threads to finish. (The order doesn't matter.) 
        foreach (Thread t in threads)
        {
            t.Join();
        }

        Console.WriteLine(
            "\r\nThreads are complete. Running GC.Collect() to reclaim extra instances.");

        GC.Collect();

        // Allow time for garbage collection, which happens asynchronously.
        Thread.Sleep(100);

        Console.WriteLine("\r\nNote that only one instance of LargeObject was used.");
        Console.WriteLine("Press Enter to end the program");
        Console.ReadLine();
    }


    static void ThreadProc(object state)
    {
        // Wait for the signal.
        ManualResetEvent waitForStart = (ManualResetEvent) state;
        waitForStart.WaitOne();

        LargeObject large = null;
        try
        {
            large = lazyLargeObject.Value;

            // The following line introduces an artificial delay, to exaggerate the race  
            // condition.
            Thread.Sleep(5); 

            // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the   
            //            object after creation. You must lock the object before accessing it, 
            //            unless the type is thread safe. (LargeObject is not thread safe.) 
            lock(large)
            {
                large.Data[0] = Thread.CurrentThread.ManagedThreadId;
                Console.WriteLine("LargeObject was initialized by thread {0}; last used by thread {1}.", 
                    large.InitializedBy, large.Data[0]);
            }
        }
        catch (ApplicationException ex)
        {
            Console.WriteLine("ApplicationException: {0}", ex.Message);
        }
    }
}

class LargeObject
{
    int initBy = -1;
    public int InitializedBy { get { return initBy; } }

    public LargeObject(int initializedBy) 
    { 
        initBy = initializedBy;
        Console.WriteLine("Constructor: Instance initializing on thread {0}", initBy);
    }

    ~LargeObject()
    {
        Console.WriteLine("Finalizer: Instance was initialized on {0}", initBy);
    }

    public long[] Data = new long[100000000];
}

/* This example produces output similar to the following:

Constructor: Instance initializing on thread 5
Constructor: Instance initializing on thread 4
ApplicationException: Lazy initialization function failed on thread 3.
LargeObject was initialized by thread 5; last used by thread 5.
LargeObject was initialized by thread 5; last used by thread 4.

Threads are complete. Running GC.Collect() to reclaim extra instances.
Finalizer: Instance was initialized on 4

Note that only one instance of LargeObject was used.
Press Enter to end the program

Finalizer: Instance was initialized on 5
 */

.NET Framework

Supported in: 4.5.2, 4.5.1, 4.5, 4

.NET Framework Client Profile

Supported in: 4

Portable Class Library

Supported in: Portable Class Library

.NET for Windows Store apps

Supported in: Windows 8

.NET for Windows Phone apps

Supported in: Windows Phone 8.1, Windows Phone 8, Silverlight 8.1

Windows Phone 8.1, Windows Phone 8, Windows 8.1, Windows Server 2012 R2, Windows 8, Windows Server 2012, Windows 7, Windows Vista SP2, Windows Server 2008 (Server Core Role not supported), Windows Server 2008 R2 (Server Core Role supported with SP1 or later; Itanium not supported)

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.

Show:
© 2014 Microsoft