Lazy(Of T) Constructor (Func(Of T), LazyThreadSafetyMode)

 

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

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

Public Sub New (
	valueFactory As Func(Of T),
	mode As LazyThreadSafetyMode
)

Parameters

valueFactory
Type: System.Func(Of 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.

Exception Condition
ArgumentOutOfRangeException

mode contains an invalid value.

ArgumentNullException

valueFactory is null.

The thread safety mode of a Lazy(Of T) instance describes the behavior when multiple threads try to initialize the Lazy(Of T) instance.

Exceptions that are thrown by valueFactory are cached, unless mode is PublicationOnly. For more information, see the Lazy(Of 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.

System_CAPS_noteNote

For an example that demonstrates how to use this constructor in single-threaded scenarios (specifying LazyThreadSafetyMode.None for mode), see the Lazy(Of 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(Of 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(Of T) object that performs lazy initialization of the LargeObject:

lazyLargeObject = New Lazy(Of LargeObject)(AddressOf 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:

Dim large As LargeObject = Nothing
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.)
    SyncLock 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))
    End SyncLock
Catch ex As ApplicationException
    Console.WriteLine("ApplicationException: {0}", ex.Message)
End Try

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:

Private Shared instanceCount As Integer = 0
Private Shared Function InitLargeObject() As LargeObject
    If 1 = Interlocked.Increment(instanceCount) Then
        Throw New ApplicationException( _
            "Lazy initialization function failed on thread " & _
            Thread.CurrentThread.ManagedThreadId & ".")
    End If
    Return New LargeObject(Thread.CurrentThread.ManagedThreadId)
End Function

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.

System_CAPS_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(Of 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 Sub New(ByVal initializedBy As Integer)
    initBy = initializedBy
    Console.WriteLine("Constructor: Instance initializing on thread {0}", initBy)
End Sub

Protected Overrides Sub Finalize()
    Console.WriteLine("Finalizer: Instance was initialized on {0}", initBy)
End Sub

The Lazy(Of 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.

System_CAPS_noteNote

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

Imports System
Imports System.Threading

Friend Class Program
    Private Shared lazyLargeObject As Lazy(Of LargeObject) = Nothing

    ' Factory function for lazy initialization.
    Private Shared instanceCount As Integer = 0
    Private Shared Function InitLargeObject() As LargeObject
        If 1 = Interlocked.Increment(instanceCount) Then
            Throw New ApplicationException( _
                "Lazy initialization function failed on thread " & _
                Thread.CurrentThread.ManagedThreadId & ".")
        End If
        Return New LargeObject(Thread.CurrentThread.ManagedThreadId)
    End Function

    Shared Sub Main()
        ' The lazy initializer is created here. LargeObject is not created until the 
        ' ThreadProc method executes.
        lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
             LazyThreadSafetyMode.PublicationOnly)


        ' Create and start 3 threads, passing the same blocking event to all of them.
        Dim startingGate As New ManualResetEvent(False)
        Dim threads() As Thread = { _
            New Thread(AddressOf ThreadProc), _
            New Thread(AddressOf ThreadProc), _
            New Thread(AddressOf ThreadProc) _
        }
        For Each t As Thread In threads
            t.Start(startingGate)
        Next t

        ' 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.)
        For Each t As Thread In threads
            t.Join()
        Next t

        Console.WriteLine(vbCrLf & _
            "Threads are complete. Running GC.Collect() to reclaim extra instances.")

        GC.Collect()

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

        Console.WriteLine(vbCrLf & "Note that only one instance of LargeObject was used.")
        Console.WriteLine("Press Enter to end the program")
        Console.ReadLine()
    End Sub


    Private Shared Sub ThreadProc(ByVal state As Object)
        ' Wait for the signal.
        Dim waitForStart As ManualResetEvent = CType(state, ManualResetEvent)
        waitForStart.WaitOne()

        Dim large As LargeObject = Nothing
        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.)
            SyncLock 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))
            End SyncLock
        Catch ex As ApplicationException
            Console.WriteLine("ApplicationException: {0}", ex.Message)
        End Try
    End Sub
End Class

Friend Class LargeObject
    Private initBy As Integer = -1
    Public ReadOnly Property InitializedBy() As Integer
        Get
            Return initBy
        End Get
    End Property

    Public Sub New(ByVal initializedBy As Integer)
        initBy = initializedBy
        Console.WriteLine("Constructor: Instance initializing on thread {0}", initBy)
    End Sub

    Protected Overrides Sub Finalize()
        Console.WriteLine("Finalizer: Instance was initialized on {0}", initBy)
    End Sub

    Public Data(99999999) As Long
End Class

' This example produces output similar to the following:
'
'Constructor: Instance initializing on thread 4
'ApplicationException: Lazy initialization function failed on thread 3.
'Constructor: Instance initializing on thread 5
'LargeObject was initialized by thread 4; last used by thread 4.
'LargeObject was initialized by thread 4; last used by thread 5.
'
'Threads are complete. Running GC.Collect() to reclaim extra instances.
'Finalizer: Instance was initialized on 5
'
'Note that only one instance of LargeObject was used.
'Press Enter to end the program
'
'Finalizer: Instance was initialized on 4
' 

Universal Windows Platform
Available since 8
.NET Framework
Available since 4.0
Portable Class Library
Supported in: portable .NET platforms
Silverlight
Available since 4.0
Windows Phone Silverlight
Available since 8.0
Windows Phone
Available since 8.1
Return to top
Show: