Skip to content

Commit 6131a03

Browse files
authored
Lazy<T> F# snippets (#7775)
1 parent 4db5cd7 commit 6131a03

File tree

11 files changed

+614
-0
lines changed

11 files changed

+614
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
module example
2+
3+
//<SnippetAll>
4+
open System
5+
open System.Threading
6+
7+
type LargeObject() =
8+
let initBy = Thread.CurrentThread.ManagedThreadId
9+
do
10+
printfn $"LargeObject was created on thread id {initBy}."
11+
12+
member val Data = Array.zeroCreate<int64> 100000000 with get
13+
member _.InitializedBy = initBy
14+
15+
// The lazy initializer is created here. LargeObject is not created until the
16+
// ThreadProc method executes.
17+
//<SnippetNewLazy>
18+
let lazyLargeObject = Lazy<LargeObject>()
19+
20+
// The following lines show how to use other constructors to achieve exactly the
21+
// same result as the previous line:
22+
// let lazyLargeObject = Lazy<LargeObject>(true)
23+
// let lazyLargeObject = Lazy<LargeObject>(LazyThreadSafetyMode.ExecutionAndPublication)
24+
//</SnippetNewLazy>
25+
26+
let threadProc (state: obj) =
27+
// Wait for the signal.
28+
let waitForStart = state :?> ManualResetEvent
29+
waitForStart.WaitOne() |> ignore
30+
31+
//<SnippetValueProp>
32+
let large = lazyLargeObject.Value
33+
//</SnippetValueProp>
34+
35+
// The following line introduces an artificial delay to exaggerate the race condition.
36+
Thread.Sleep 5
37+
38+
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
39+
// object after creation. You must lock the object before accessing it,
40+
// unless the type is thread safe. (LargeObject is not thread safe.)
41+
lock large (fun () ->
42+
large.Data[0] <- Thread.CurrentThread.ManagedThreadId
43+
printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}." )
44+
45+
printfn """
46+
LargeObject is not created until you access the Value property of the lazy
47+
initializer. Press Enter to create LargeObject."""
48+
stdin.ReadLine() |> ignore
49+
50+
// Create and start 3 threads, passing the same blocking event to all of them.
51+
let startingGate = new ManualResetEvent false
52+
let threads = [| Thread(ParameterizedThreadStart threadProc); Thread(ParameterizedThreadStart threadProc); Thread(ParameterizedThreadStart threadProc) |]
53+
for t in threads do
54+
t.Start startingGate
55+
56+
// Give all 3 threads time to start and wait, then release them all at once.
57+
Thread.Sleep 100
58+
startingGate.Set() |> ignore
59+
60+
// Wait for all 3 threads to finish. (The order doesn't matter.)
61+
for t in threads do
62+
t.Join()
63+
64+
printfn "\nPress Enter to end the program"
65+
stdin.ReadLine() |> ignore
66+
67+
// This example produces output similar to the following:
68+
// LargeObject is not created until you access the Value property of the lazy
69+
// initializer. Press Enter to create LargeObject.
70+
//
71+
// LargeObject was created on thread id 4.
72+
// Initialized by thread 4 last used by thread 3.
73+
// Initialized by thread 4 last used by thread 4.
74+
// Initialized by thread 4 last used by thread 5.
75+
//
76+
// Press Enter to end the program
77+
//</SnippetAll>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module example1
2+
3+
//<SnippetAll>
4+
open System
5+
open System.Threading
6+
7+
type LargeObject () =
8+
do
9+
printfn $"LargeObject was created on thread id {Thread.CurrentThread.ManagedThreadId}."
10+
11+
member val Data = Array.zeroCreate<int64> 100000000 with get
12+
13+
// The lazy initializer is created here. LargeObject is not created until the
14+
// ThreadProc method executes.
15+
//<SnippetNewLazy>
16+
let lazyLargeObject = Lazy<LargeObject> false
17+
// The following lines show how to use other constructors to achieve exactly the
18+
// same result as the previous line:
19+
// let lazyLargeObject = Lazy<LargeObject>(LazyThreadSafetyMode.None)
20+
//</SnippetNewLazy>
21+
22+
printfn """
23+
LargeObject is not created until you access the Value property of the lazy
24+
initializer. Press Enter to create LargeObject."""
25+
stdin.ReadLine() |> ignore
26+
27+
//<SnippetValueProp>
28+
let large = lazyLargeObject.Value
29+
//</SnippetValueProp>
30+
31+
large.Data[11] <- 89
32+
33+
printfn "\nPress Enter to end the program"
34+
stdin.ReadLine() |> ignore
35+
36+
37+
// This example produces output similar to the following:
38+
// LargeObject is not created until you access the Value property of the lazy
39+
// initializer. Press Enter to create LargeObject.
40+
//
41+
// LargeObject was created on thread id 1.
42+
//
43+
// Press Enter to end the program
44+
//</SnippetAll>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
module example2
2+
3+
//<SnippetAll>
4+
open System
5+
open System.Threading
6+
7+
//<SnippetLargeCtor>
8+
type LargeObject() =
9+
static let mutable instanceCount = 0
10+
let initBy = Thread.CurrentThread.ManagedThreadId
11+
do
12+
if 1 = Interlocked.Increment &instanceCount then
13+
raise (ApplicationException "Throw only ONCE.")
14+
printfn $"LargeObject was created on thread id {initBy}."
15+
//</SnippetLargeCtor>
16+
member _.InitializedBy = initBy
17+
member val Data = Array.zeroCreate<int64> 100000000
18+
19+
//<SnippetFactoryFunc>
20+
let initLargeObject () =
21+
LargeObject()
22+
//</SnippetFactoryFunc>
23+
24+
// The lazy initializer is created here. LargeObject is not created until the
25+
// ThreadProc method executes.
26+
//<SnippetNewLazy>
27+
let lazyLargeObject = Lazy<LargeObject> initLargeObject
28+
29+
// The following lines show how to use other constructors to achieve exactly the
30+
// same result as the previous line:
31+
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
32+
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, LazyThreadSafetyMode.ExecutionAndPublication)
33+
//</SnippetNewLazy>
34+
35+
let threadProc _ =
36+
//<SnippetValueProp>
37+
try
38+
let large = lazyLargeObject.Value
39+
40+
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
41+
// object after creation. You must lock the object before accessing it,
42+
// unless the type is thread safe. (LargeObject is not thread safe.)
43+
lock large (fun () ->
44+
large.Data[0] <- Thread.CurrentThread.ManagedThreadId
45+
printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
46+
with :? ApplicationException as aex ->
47+
printfn $"Exception: {aex.Message}"
48+
//</SnippetValueProp>
49+
50+
printfn """
51+
LargeObject is not created until you access the Value property of the lazy
52+
initializer. Press Enter to create LargeObject."""
53+
stdin.ReadLine () |> ignore
54+
55+
// Create and start 3 threads, each of which tries to use LargeObject.
56+
let threads =
57+
[| Thread(ParameterizedThreadStart threadProc); Thread(ParameterizedThreadStart threadProc); Thread(ParameterizedThreadStart threadProc) |]
58+
for t in threads do
59+
t.Start()
60+
61+
// Wait for all 3 threads to finish. (The order doesn't matter.)
62+
for t in threads do
63+
t.Join()
64+
65+
printfn "\nPress Enter to end the program"
66+
stdin.ReadLine() |> ignore
67+
68+
// This example produces output similar to the following:
69+
// LargeObject is not created until you access the Value property of the lazy
70+
// initializer. Press Enter to create LargeObject.
71+
//
72+
// Exception: Throw only ONCE.
73+
// Exception: Throw only ONCE.
74+
// Exception: Throw only ONCE.
75+
//
76+
// Press Enter to end the program
77+
//</SnippetAll>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
module example3
2+
3+
//<SnippetAll>
4+
open System
5+
open System.Threading
6+
7+
//<SnippetLargeCtor>
8+
type LargeObject() =
9+
static let mutable pleaseThrow = true
10+
do
11+
if pleaseThrow then
12+
pleaseThrow <- false
13+
raise (ApplicationException "Throw only ONCE.")
14+
printfn $"LargeObject was created on thread id {Thread.CurrentThread.ManagedThreadId}."
15+
//</SnippetLargeCtor>
16+
member val Data = Array.zeroCreate<int64> 100000000
17+
18+
//<SnippetFactoryFunc>
19+
let initLargeObject () =
20+
LargeObject()
21+
//</SnippetFactoryFunc>
22+
23+
// The lazy initializer is created here. LargeObject is not created until the
24+
// ThreadProc method executes.
25+
//<SnippetNewLazy>
26+
let lazyLargeObject = Lazy<LargeObject>(initLargeObject, false)
27+
28+
// The following lines show how to use other constructors to achieve exactly the
29+
// same result as the previous line:
30+
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, LazyThreadSafetyMode.None)
31+
//</SnippetNewLazy>
32+
33+
printfn """
34+
LargeObject is not created until you access the Value property of the lazy
35+
initializer. Press Enter to create LargeObject (three tries)."""
36+
stdin.ReadLine() |> ignore
37+
38+
//<SnippetValueProp>
39+
for _ = 0 to 2 do
40+
try
41+
let large = lazyLargeObject.Value
42+
large.Data[11] <- 89
43+
with :? ApplicationException as aex ->
44+
printfn $"Exception: {aex.Message}"
45+
//</SnippetValueProp>
46+
47+
printfn "\nPress Enter to end the program"
48+
stdin.ReadLine() |> ignore
49+
50+
// This example produces output similar to the following:
51+
// LargeObject is not created until you access the Value property of the lazy
52+
// initializer. Press Enter to create LargeObject (three tries).
53+
//
54+
// Exception: Throw only ONCE.
55+
// Exception: Throw only ONCE.
56+
// Exception: Throw only ONCE.
57+
//
58+
// Press Enter to end the program
59+
//</SnippetAll>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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 "\nThreads 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 "\nNote 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

Comments
 (0)