一、什么是初始加载?

实现单例模式有两种方式,一种是懒加载,也就是延迟加载,当首次调用时创建单例对象,另一种是初始加载,在应用程序启动时就初始化单例对象,并将其保存在内存中以备将来使用,而不是需要时才创建。初始加载不需要考虑多线程环境导致的线程不安全问题,因为CLR将负责对象初始化和线程安全。这意味着我们不需要显式地编写任何代码来处理多线程环境的线程安全性。如下:

public sealed class Singleton
{
    private static int counter = 0;
    private static readonly Singleton singleInstance = new Singleton(); 
    private Singleton()
    {
        counter++;
        Console.WriteLine("Counter Value " + counter.ToString());
    }
    
    public static Singleton GetInstance
    {
        get
        {
            return singleInstance;
        }
    }
    public void PrintDetails(string message)
    {
        Console.WriteLine(message);
    }
}

定义私有静态常量singleInstance,并实例化Singleton对象赋值给该常量,由于是静态的,所以在应用启动就会创建,并加载到内存中,可以随时调用。主要由CLR在内部负责变量的初始化以及即时加载中的线程安全。

二、什么是延迟加载?

定义私有静态常量,但是并不实例化赋值,而是等到首次调用时再创建实例,往后每次调用就用这一个实例。也就是将对象的初始化延迟到需要它的时候。当创建对象的成本非常高以及很少使用该对象时,需要使用延迟加载。如果使用得当,延迟加载可以提高应用程序的性能。在C#中可以使用Lazy关键字使单例实例延迟加载。

三、理解C#中的Lazy关键字

Lazy是在.NET Framework 4.0引入的,提供对延迟初始化的支持。使用方式如下:

private static readonly Lazy<Singleton> Instancelock = new Lazy<Singleton>(() => new Singleton());

非常重要的一点是: Lazy<T> 对象在默认情况下是线程安全的。在多线程环境中,当多个线程试图同时访问相同的 GetInstance 属性时,Lazy将负责线程安全。代码如下:

public sealed class Singleton
{
    private static int counter = 0;
    private static readonly Lazy<Singleton> Instancelock 
        = new Lazy<Singleton>(() => new Singleton());

    private Singleton()
    {
        counter++;
        Console.WriteLine("Counter Value " + counter.ToString());
    }
    public static Singleton GetInstance
    {
        get
        {
            return Instancelock.Value;
        }
    }
    public void PrintDetails(string message)
    {
        Console.WriteLine(message);
    }
}

最后输出的结果和初始加载运行输出的结果是相同的,这是因为Lazy关键字只创建一个单例实例,而且它们在默认情况下是线程安全的。

 

C#之Lazy<T>

Lazy<T>主要用在单例模式,是一种延迟加载(Lazy Loading)的机制,它允许您推迟对象的创建直到第一次访问该对象。这种方式在需要时才分配资源,能够提高性能和资源利用率。Lazy类提供了一个简单且线程安全的方法来实现延迟加载。以下是Lazy的一些主要特点和用法:
延迟加载:Lazy允许您将对象的创建推迟到第一次访问该对象的时候,而不是在对象被创建时就立即分配资源。

线程安全:Lazy在多线程环境下是线程安全的,它会确保只有一个线程创建对象,其他线程在对象创建完成之前会被阻塞。

性能优化:Lazy的延迟加载机制能够在应用程序启动时节省资源,只有在需要时才会执行实际的对象创建过程。

异常处理:如果对象的创建过程中发生了异常,Lazy会捕获并将异常包装在后续访问对象时抛出。

支持初始化:Lazy提供了多种构造函数,允许您传入一个委托来初始化对象,或者指定延迟加载的行为。

int count = 0;
Lazy<string> lazyStr = new Lazy<string>(() =>
{
    Console.WriteLine("Lasy Loading...Only once,Only needed!");
    count += 1;
    return "Hello,LasyStr";
});
Console.WriteLine($"Try to fetch lasyStr:{lazyStr.Value}");
Console.WriteLine($"Try to fetch lasyStr again:{lazyStr.Value}");
Console.WriteLine($"Try to fetch lasyStr again:{lazyStr.Value}");
Console.WriteLine($"now,count is {count}");