[C# thread] delay initialization

 

1. Introduction

1. Delayed initialization occurs at NET 4.0, which is mainly used to improve performance, avoid wasting computing, and reduce program memory requirements. It can also be called on-demand loading.

2. Starting from net 4.0, C# began to support delayed initialization. Through the Lazy keyword, we can declare that an object is initialized only when it is used for the first time. If it has not been called, it will not be initialized, saving some unnecessary overhead and improving efficiency. At the same time, Lazy is inherently thread safe

3. Note: lazy < T > object initialization is thread safe by default. In a multithreaded environment, the first thread accessing the Value attribute of the lazy < T > object will initialize the lazy < T > object, and subsequent threads will use the data of the first initialization.

2. Application scenarios

  • 1. Large and time consuming objects

    Object creation is expensive and programs may not use it. For example, suppose that there is a Customer object with Orders attribute in memory, which contains a large number of Order objects. Initializing these objects requires a database connection. If the user never asks to display Orders or use the data in the calculation, it does not need to use system memory or calculation cycles to create it. By using lazy < Orders > to declare the Orders object for slow initialization, you can avoid wasting system resources when the object is not used. Object creation is expensive and you want to postpone its creation until other high-cost operations are completed. For example, suppose a program loads multiple object instances at startup, but only some of them need to be loaded immediately. The startup performance of the program can be improved by delaying the initialization of unnecessary objects until the required objects are created.

  • 2. Packaging method

    Save the delegate or method object and call it when needed.

Although you can write your own code to perform lazy initialization, we recommend using Lazy<T>. Lazy<T> And its related types also support thread safety and provide a consistent exception propagation strategy.

The following table lists NET Framework version 4 provides types that enable lazy initialization in different scenarios.

Table 1
typeexplain
Lazy<T> A wrapper class that provides lazy initialization semantics for any class library or user-defined type.
ThreadLocal<T> be similar to Lazy<T> , except that the wrapper class provides lazy initialization semantics based on thread locality. Each thread can access its own unique value.
LazyInitializer Provide advanced static (Shared in Visual Basic) method for slow initialization of objects without paying the cost of classes.

 

 

 

 

 

 

 

Lazy constructor

Constructor
Lazy<T>()

initialization Lazy<T> Class. When a lazy initialization occurs, a parameterless constructor of the target type is used.

Lazy<T>(Boolean)

initialization Lazy<T> Class. When lazy initialization occurs, the parameterless constructor of the target type and the specified initialization mode are used.

Lazy<T>(Func<T>)

initialization Lazy<T> Class. When slow initialization occurs, the specified initialization function is used.

Lazy<T>(Func<T>, Boolean)

initialization Lazy<T> Class. When delayed initialization occurs, the specified initialization function and initialization mode are used.

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

initialization Lazy<T> Class, which uses the specified initialization function and thread safe mode.

Lazy<T>(LazyThreadSafetyMode)

initialization Lazy<T> Class, which uses T's parameterless constructor and the specified thread safety mode.

Lazy<T>(T)

initialization Lazy<T> Class that uses a specified value that has been pre initialized.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Lazy. Use of the value property

After a Lazy object is created, the corresponding object will not be created immediately. Once used Value, the corresponding variable will be instantiated, and the IsValueCreated attribute will become true.

The Value property is read-only, which means that if the Value stores a reference type, it cannot be assigned a new object. Only the public properties or fields of the object can be changed. If the Value stores a Value type, its Value cannot be modified. A new variable can only be created by calling the function of the variable again with a new parameter

After the Lazy object is created, before accessing the Value property of the variable for the first time,

Lazy. Use of isvaluecreated property

Judgment is instantiation

LazyThreadSafetyMode enumeration

appoint Lazy<T> Instance how to synchronize access between multiple threads.

 

All such constructors are fully thread safe.

If locks are used internally, deadlocks may occur

ExecutionAndPublication  
Not thread safe. None false Not applicable.
Fully thread safe; Thread contention for initialization value. PublicationOnly

 

 

 

 

The default initialization method is used when constructing

using System;

namespace LazyUsage
{
    class LazyDemo
    {
        static void Main()
        {
            Lazy<Data> lazyData = new Lazy<Data>();
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);
            lazyData.Value.Print();//Only when accessed here Data Real initialization
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);

            Console.ReadKey();
        }
    }

    class Data
    {
        public Data() 
        {
            Console.WriteLine("Data::.ctor->Initialized");
        }

        public void Print()
        {
            Console.WriteLine("Data::Print->println");
        }
    }
}

Output results

Main->is lazyData Initialized? value = False
Data::.ctor->Initialized
Data::Print->println
Main->is lazyData Initialized? value = True

Class with the specified delegate when constructing

using System;

namespace LazyUsage
{
    class LazyDemo
    {
        static void Main()
        {
            //Specifies the delegate to initialize Data
            Lazy<Data> lazyData = new Lazy<Data>(
                () =>
                {
                    Console.WriteLine("Main->lazyData will be Initialized!");
                    return new Data("Test");
                });
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);
            lazyData.Value.Print();
            Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);


            Console.ReadKey();
        }
    }

    class Data
    {
        public string Name { get; private set; }

        public Data(string name)
        {
            Name = name;
            Console.WriteLine("Data::.ctor->Initialized,name = "+name);
        }

        public void Print()
        {
            Console.WriteLine("Data::Print->name = " + Name);
        }
    }
}

Output results

Main->is lazyData Initialized? value = False
Main->lazyData will be Initialized!
Data::.ctor->Initialized,name = Test
Data::Print->name = Test
Main->is lazyData Initialized? value = True

 

Expansion Implementation delay attribute

class Customer
{
    private Lazy<Orders> _orders;

    public string CustomerID {get; private set;}

    public Customer(string id)
    {
        CustomerID = id;
        _orders = new Lazy<Orders>(() =>
        {
            return new Orders(this.CustomerID);
        });
    }

    public Orders MyOrders
    {
        get
        {
            return _orders.Value;
        }
    }
}

ThreadLocal < T > class

This class is equivalent to a thread boundary, which limits the value and function of variables to threads. So the types wrapped in this class are thread safe. The wrapped type variables of this class can only be used in this thread. Other threads, including child threads, cannot use them.

Constructor

Constructor
ThreadLocal<T>()

initialization ThreadLocal<T> example.

ThreadLocal<T>(Boolean)

initialization ThreadLocal<T> Instance and specify whether all values can be accessed from any thread.

ThreadLocal<T>(Func<T>)

Class using the specified valueFactory function ThreadLocal<T> example.

ThreadLocal<T>(Func<T>, Boolean)

Class with the specified valueFactory function and a flag indicating whether all values can be accessed from any thread ThreadLocal<T> example.

 

 

 

 

 

attribute

attribute
IsValueCreated

Gets whether to initialize on the current thread Value.

Value

Gets or sets the value of this instance of the current thread.

Values

Gets a list of all values currently stored by all threads that have accessed this instance.

 

 

 

 

 

 

 

Provides local storage of thread data.

static void Main(string[] args)
{
     ThreadLocal<int> threadLocal = new ThreadLocal<int>();
     //In the main thread, the value of this variable is 1
     threadLocal.Value = 1;
     new Thread(() => Console.WriteLine($"managed thread  ID: {Thread.CurrentThread.ManagedThreadId} The value is:{threadLocal.Value++}")).Start();
     new Thread(() => Console.WriteLine($"managed thread  ID: {Thread.CurrentThread.ManagedThreadId} The value is:{threadLocal.Value++}")).Start();
     new Thread(() => Console.WriteLine($"managed thread  ID: {Thread.CurrentThread.ManagedThreadId} The value is:{threadLocal.Value++}")).Start();
     Console.WriteLine($"Main thread ID: {Thread.CurrentThread.ManagedThreadId} The value is:{threadLocal.Value}");
}

Output results:
Managed thread ID: 10 value: 0
Main thread ID: 1 Value: 1
Managed thread ID: 11 value: 0
Managed thread ID: 12 value: 0

 

using System;
using System.Threading;
using System.Threading.Tasks;

class ThreadLocalDemo
{
    
        // Demonstrates:
        //      ThreadLocal(T) constructor
        //      ThreadLocal(T).Value
        //      One usage of ThreadLocal(T)
        static void Main()
        {
            // Thread-Local variable that yields a name for a thread
            ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
            {
                return "Thread" + Thread.CurrentThread.ManagedThreadId;
            });

            // Action that prints out ThreadName for the current thread
            Action action = () =>
            {
                // If ThreadName.IsValueCreated is true, it means that we are not the
                // first action to run on this thread.
                bool repeat = ThreadName.IsValueCreated;

                Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
            };

            // Launch eight of them.  On 4 cores or less, you should see some repeat ThreadNames
            Parallel.Invoke(action, action, action, action, action, action, action, action);

            // Dispose when you are done
            ThreadName.Dispose();
        }
}
// This multithreading example can produce different outputs for each 'action' invocation and will vary with each run.
// Therefore, the example output will resemble but may not exactly match the following output (from a 4 core processor):
// ThreadName = Thread5 
// ThreadName = Thread6 
// ThreadName = Thread4 
// ThreadName = Thread6 (repeat)
// ThreadName = Thread1 
// ThreadName = Thread4 (repeat)
// ThreadName = Thread7 
// ThreadName = Thread5 (repeat)

 

Keywords: C#

Added by ibo on Tue, 28 Dec 2021 15:18:49 +0200