Thread-safe Lazy initialization examples in C#.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Concurrent;

class Program
{
    private static void Main(string[] args)
    {
        ThreadSafeInit();
        ThreadLocalLazyInit();
    }

    //
    // Thread-Safe Lazy Initialization
    //
    // The following example shows that the same Lazy<int> instance has the same value for three separate threads.
    // https://docs.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization#thread-safe-initialization
    private static void ThreadSafeInit()
    {
        Console.WriteLine("Thread-Safe Lazy Initialization:");
        Console.WriteLine("");
        // Initialize the integer to the managed thread id of the
        // first thread that accesses the Value property.
        Lazy<int> number = new Lazy<int>(() => Thread.CurrentThread.ManagedThreadId);

        Thread t1 = new Thread(() => Console.WriteLine("- number on t1 = {0} ThreadID = {1}",
                            number.Value, Thread.CurrentThread.ManagedThreadId));
        t1.Start();

        Thread t2 = new Thread(() => Console.WriteLine("- number on t2 = {0} ThreadID = {1}",
                            number.Value, Thread.CurrentThread.ManagedThreadId));
        t2.Start();

        Thread t3 = new Thread(() => Console.WriteLine("- number on t3 = {0} ThreadID = {1}", number.Value,
                            Thread.CurrentThread.ManagedThreadId));
        t3.Start();

        // Ensure that thread IDs are not recycled if the
        // first thread completes before the last one starts.
        t1.Join();
        t2.Join();
        t3.Join();
        
        Console.WriteLine("");
    }

    //
    // Thread-Local Lazy Initialization
    //
    // The following example demonstrates that every thread that accesses the ThreadLocal<int> instance gets its own unique copy of the data.
    // https://docs.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization#thread-local-lazy-initialization
    private static void ThreadLocalLazyInit()
    {
        Console.WriteLine("Thread-Local Lazy Initialization:");
        Console.WriteLine("");

        // Initialize the integer to the managed thread id on a per-thread basis.
        ThreadLocal<int> threadLocalNumber = new ThreadLocal<int>(() => Thread.CurrentThread.ManagedThreadId);
        Thread t4 = new Thread(() => Console.WriteLine("- threadLocalNumber on t4 = {0} ThreadID = {1}",
                                                        threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
        t4.Start();

        Thread t5 = new Thread(() => Console.WriteLine("- threadLocalNumber on t5 = {0} ThreadID = {1}",
                                                        threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
        t5.Start();

        Thread t6 = new Thread(() => Console.WriteLine("- threadLocalNumber on t6 = {0} ThreadID = {1}",
                                                        threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
        t6.Start();

        // Ensure that thread IDs are not recycled if the
        // first thread completes before the last one starts.
        t4.Join();
        t5.Join();
        t6.Join();

        /* Sample Output:
            threadLocalNumber on t4 = 14 ThreadID = 14
            threadLocalNumber on t5 = 15 ThreadID = 15
            threadLocalNumber on t6 = 16 ThreadID = 16
        */
    }
}