2

.NET进阶:Lazy 延迟创建的容器🛏

我们经常使用的容器有List、Dictionary、Set。这里来聊聊一个比较另类的存储容器 Lazy☂。

在C#中,Lazy<T> 类是一个线程安全的延迟初始化容器,它用于延迟对象的创建直到真正需要该对象时。这可以帮助减少不必要的对象创建,节省内存和提高性能。

Lazy相比于其他容器而言,其实就像是一个包裹在外层的初始化器,而不是单纯存储数据的容器。

我们先来看看Lazy的使用。

var sunsun = new Lazy<SunLuckSun>(() => { 
    return new SunLuckSun(); 
});

通过上述的代码,你就能延迟声明一个🌞阳阳同学。

如果你看了我之前讲解的linq的使用,或者你了解linq的延迟执行特性。那么就不难理解什么叫延迟执行。

你只有在使用到封装的Lazy中的值的时候,这段声明内容才会真正被执行。使用lazy的值非常简单,

sunsun.Value

就能运行对应的生成对象的过程,以及获取到对应的对象。
也就是说,你从未使用sunsun.Value的情况下,不会执行new SunLuckSun()这个操作。

封装一层的好处就在于,🍉生成对象的过程只会运行一次🍉,其他形式的调用,只会拿取第一次生成的结果。如果你的程序中用到多线程,且所有线程都使用Lazy,那么只有第一次使用Value的时候会执行Lazy初始化中放入的委托,其他每次都是拿取同一个值。

那么我们应该如何知道执行过程是否执行过呢?那就是使用IsValueCreated。这个属性是一个bool值,用于判断Value值是否被生成。

Lazy本身是线程安全的。当然,你可以让他不符合线程安全。

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

这个重载中,第一参数还是一个生成对应值的委托,第二参数是指定是否保证线程安全,如果你是个单线程项目,那就给个false吧。

Lazy(Func<T>, LazyThreadSafetyMode)

你还可以设置线程安全的模式,这个值有三个枚举值。

LazyThreadSafetyMode.None;
LazyThreadSafetyMode.PublicationOnly;
LazyThreadSafetyMode.ExecutionAndPublication;

在VS中使用这一重载,有智能提示,会有一大段的内容描述这三种模式有什么用。

这里简单说明一下这几种模式:
首先第一种,不使用线程安全。

第二种,如果多线程同时运行value,并且value值的生成过程未被执行。则所有线程都同时执行生成过程,然后lazy的value值为第一个执行完毕的结果。

还是举一个生动形象的例子吧🍓,你向一些同学发布了一个作业,让他们同时开始写✍。然后你的职责是告诉同学们,这道题目的正确答案。PublicationOnly模式,就是第一个向你交📃作业的同学,你告诉他正确答案就是他写的。之后任何同学来交作业,你都会给出第一位同学的答案,无视这位同学交上来的答案。

这其中,每个线程都是相互独立的,如果有位同学没写出来(生成过程中引发了异常),不会影响到其他同学答题。异常会在所有线程执行完成后再被扔出。

第三种,如果多线程同时运行value,并且value值的生成过程未被执行。上锁并依次执行生成过程。

还是写作业这个例子,不同点在于,这次不是所有同学一起写,而是一个个依次上黑板来写。第一个人写出来了,其他人不用写了,答案都是同一个。如果第一个人没写出来,抛出了一个异常,那么所有人都会扔出同样的异常,并且生成过程执行失败。

黑板就是锁🔏,这种模式下生成过程只有一个线程执行一遍,成功则所有线程拿取同样的值。失败,则执行线程扔出的异常会影响所有线程,进而导致生成值失败。


wang_hun
9 声望1 粉丝