1+ //<SnippetAll>
2+ open System
3+ open System.Threading
4+
5+ //<SnippetCtorFinalizer>
6+ type LargeObject ( initBy ) =
7+ do
8+ printfn $" Constructor: Instance initializing on thread {initBy}"
9+
10+ override _.Finalize () =
11+ printfn $" Finalizer: Instance was initialized on {initBy}"
12+ //</SnippetCtorFinalizer>
13+ member _.InitializedBy = initBy
14+ member val Data = Array.zeroCreate< int64> 100000000 with get
15+
16+ // Factory function for lazy initialization.
17+ //<SnippetFactoryFunc>
18+ let mutable instanceCount = 0
19+ let initLargeObject () =
20+ if 1 = Interlocked.Increment & instanceCount then
21+ raise ( ApplicationException $" Lazy initialization function failed on thread {Thread.CurrentThread.ManagedThreadId}." )
22+ LargeObject Thread.CurrentThread.ManagedThreadId
23+ //</SnippetFactoryFunc>
24+
25+ // The lazy initializer is created here. LargeObject is not created until the
26+ // ThreadProc method executes.
27+ //<SnippetNewLazy>
28+ let lazyLargeObject = Lazy< LargeObject>( initLargeObject, LazyThreadSafetyMode.PublicationOnly)
29+ //</SnippetNewLazy>
30+
31+ let threadProc ( state : obj ) =
32+ // Wait for the signal.
33+ let waitForStart = state :?> ManualResetEvent
34+ waitForStart.WaitOne() |> ignore
35+
36+ //<SnippetValueProp>
37+ try
38+ let large = lazyLargeObject.Value
39+
40+ // The following line introduces an artificial delay to exaggerate the race condition.
41+ Thread.Sleep 5
42+
43+ // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
44+ // object after creation. You must lock the object before accessing it,
45+ // unless the type is thread safe. (LargeObject is not thread safe.)
46+ lock large ( fun () ->
47+ large.Data[ 0 ] <- Thread.CurrentThread.ManagedThreadId
48+ printfn $" LargeObject was initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}." )
49+ with :? ApplicationException as ex ->
50+ printfn $" ApplicationException: {ex.Message}"
51+ //</SnippetValueProp>
52+
53+ // Create and start 3 threads, passing the same blocking event to all of them.
54+ let startingGate = new ManualResetEvent false
55+ let threads =
56+ [| Thread( ParameterizedThreadStart threadProc); Thread( ParameterizedThreadStart threadProc); Thread( ParameterizedThreadStart threadProc) |]
57+ for t in threads do
58+ t.Start startingGate
59+
60+ // Give all 3 threads time to start and wait, then release them all at once.
61+ Thread.Sleep 50
62+ startingGate.Set() |> ignore
63+
64+ // Wait for all 3 threads to finish. (The order doesn't matter.)
65+ for t in threads do
66+ t.Join()
67+
68+ printfn " \n Threads are complete. Running GC.Collect() to reclaim extra instances."
69+
70+ GC.Collect()
71+
72+ // Allow time for garbage collection, which happens asynchronously.
73+ Thread.Sleep 100
74+
75+ printfn " \n Note that only one instance of LargeObject was used."
76+ printfn " Press Enter to end the program"
77+ stdin.ReadLine() |> ignore
78+
79+ // This example produces output similar to the following:
80+ // Constructor: Instance initializing on thread 5
81+ // Constructor: Instance initializing on thread 4
82+ // ApplicationException: Lazy initialization function failed on thread 3.
83+ // LargeObject was initialized by thread 5 last used by thread 5.
84+ // LargeObject was initialized by thread 5 last used by thread 4.
85+ //
86+ // Threads are complete. Running GC.Collect() to reclaim extra instances.
87+ // Finalizer: Instance was initialized on 4
88+ //
89+ // Note that only one instance of LargeObject was used.
90+ // Press Enter to end the program
91+ //
92+ // Finalizer: Instance was initialized on 5
93+ //</SnippetAll>
0 commit comments