博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Core源码(三) Lazy<T>
阅读量:5161 次
发布时间:2019-06-13

本文共 7262 字,大约阅读时间需要 24 分钟。

Lazy<T>解决什么问题?

1、大对象加载

  考虑下面的需求,有个对象很大,创建耗时,并且要在托管堆上分配一大块空间。我们当然希望,用到它的时候再去创建。也就是延迟加载,等到真正需要它的时候,才去加载。

  显然,这里需要加一个中间层,将大对象封装起来,暴露接口,开始并不创建大对象,等到用户真正访问对象的时候,再去创建。另外,这个中间层应该可以封装不同类型的大对象,因此需要类模版。Lazy<T>就是为了解决这个问题。

  典型的使用

public Lazy
AccountServ = new Lazy
();public Lazy
ProductService = new Lazy
();

2、将委托或者方法对象保存,并在需要的时候调用。

private readonly Lazy
_connectionLazy;public CallHistoryRepository(ConnectionFactory connectionFactory){ _connectionLazy = new Lazy
(()=>connectionFactory.Connection);}

一旦使用.Vale,那么对应的变量就会被实例化,IsValueCreated属性也就变成了true。

实现自己的Lazy<T>

  在.NET Framework 4.0之前,大对象就是存在的,那么对于一个大型系统而言,怎么样对付一个大对象呢。主要有两点:延迟加载即时清理。前者解决创建问题,后者解决回收问题。

  那么在来看Lazy<T>的.NET Framework实现之前,我们先来自己实现一个简单的Lazy<T>吧。

class MyLazy
where T : new(){ private T value; private bool isLoaded; public MyLazy() { isLoaded = false; } public T Value { get { if (!isLoaded) { value = new T(); isLoaded = true; } return value; } }}

这应该是最简单版本的Lazy<T>了,没有线程安全检测,只有着访问时创建真实对象,可是对于我们一般的应用来说也许就已经足够了。

.NET Lazy<T> 实现

.NET Core和我们的实现,有两点主要的不同:

1、 引入了Boxed内部类:

[Serializable]private class Boxed{    // Fields    internal T m_value;    // Methods    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]    internal Boxed(T value)    {        this.m_value = value;    }}

  该内部类取代了我在上面实现中的泛型约束,使之更通用。

  但是我们也应该注意到,如果T为结构体,那么由于T很大,所以装箱拆箱反而也许是个更耗费效率的事情,因此,个人建议,对值类型慎用Lazy<T>

2、 线程安全的控制

在线程安全的控制选项中,.NET Framework为我们提供了这样的枚举选项:

public enum LazyThreadSafetyMode{    None,    PublicationOnly,    ExecutionAndPublication}

默认值为ExecutionAndPublication

枚举选项MSDN介绍如下

isThreadSafe则应用于多线程环境下,如果isThreadSafe为false,那么延迟加载对象则一次只能创建于一个线程。

Lazy<T>源码

System.Runtime命名空间下

由于core中的lazy源码我只找到了下图这个,再往下lazy的实现并没找

所以我使用.Net 4.5的lazy源码

 

一、最常使用的属性Value

[DebuggerBrowsable(DebuggerBrowsableState.Never)]public T Value{    get    {        Boxed boxed = null;        if (m_boxed != null )        {            // Do a quick check up front for the fast path.            boxed = m_boxed as Boxed;            if (boxed != null)            {                return boxed.m_value;            }            LazyInternalExceptionHolder exc = m_boxed as LazyInternalExceptionHolder;            Contract.Assert(m_boxed != null);            exc.m_edi.Throw();        }        return LazyInitValue();    }}//null --> value is not created//m_value is Boxed --> the value is created, and m_value holds the value//m_value is LazyExceptionHolder --> it holds an exceptionprivate object m_boxed;
View Code

如果m_boxed有值,就直接装箱返回对应值(这里就要注意值类型装箱的性能损失了)。这个装箱是为了检验下m_boxed的值,因为其中有可能是异常。

二、LazyInitValue方法

如果m_boxed为空,就调用LazyInitValue方法,这里有针对线程安全模式的判断

/// /// local helper method to initialize the value /// /// 
The inititialized T value
private T LazyInitValue(){ Boxed boxed = null; LazyThreadSafetyMode mode = Mode; if (mode == LazyThreadSafetyMode.None) { boxed = CreateValue(); m_boxed = boxed; } else if (mode == LazyThreadSafetyMode.PublicationOnly) { boxed = CreateValue(); if (boxed == null || Interlocked.CompareExchange(ref m_boxed, boxed, null) != null) { // If CreateValue returns null, it means another thread successfully invoked the value factory // and stored the result, so we should just take what was stored. If CreateValue returns non-null // but we lose the ---- to store the single value, again we should just take what was stored. boxed = (Boxed)m_boxed; } else { // We successfully created and stored the value. At this point, the value factory delegate is // no longer needed, and we don't want to hold onto its resources. m_valueFactory = ALREADY_INVOKED_SENTINEL; } } else { object threadSafeObj = Volatile.Read(ref m_threadSafeObj); bool lockTaken = false; try { if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL) Monitor.Enter(threadSafeObj, ref lockTaken); else Contract.Assert(m_boxed != null); if (m_boxed == null) { boxed = CreateValue(); m_boxed = boxed; Volatile.Write(ref m_threadSafeObj, ALREADY_INVOKED_SENTINEL); } else // got the lock but the value is not null anymore, check if it is created by another thread or faulted and throw if so { boxed = m_boxed as Boxed; if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder { LazyInternalExceptionHolder exHolder = m_boxed as LazyInternalExceptionHolder; Contract.Assert(exHolder != null); exHolder.m_edi.Throw(); } } } finally { if (lockTaken) Monitor.Exit(threadSafeObj); } } Contract.Assert(boxed != null); return boxed.m_value;}
View Code

三、CreateValue方法

返回一个实例对象T,采用传入的func方法,这里会对是否已经返回做出判断,如果已经返回,就返回null

/// Creates an instance of T using m_valueFactory in case its not null or use reflection to create a new T()/// 
An instance of Boxed.
private Boxed CreateValue(){ Boxed boxed = null; LazyThreadSafetyMode mode = Mode; if (m_valueFactory != null) { try { // check for recursion if (mode != LazyThreadSafetyMode.PublicationOnly && m_valueFactory == ALREADY_INVOKED_SENTINEL) throw new InvalidOperationException(Environment.GetResourceString("Lazy_Value_RecursiveCallsToValue")); Func
factory = m_valueFactory; if (mode != LazyThreadSafetyMode.PublicationOnly) // only detect recursion on None and ExecutionAndPublication modes { m_valueFactory = ALREADY_INVOKED_SENTINEL; } else if (factory == ALREADY_INVOKED_SENTINEL) { // Another thread ----d with us and beat us to successfully invoke the factory. return null; } boxed = new Boxed(factory()); } catch (Exception ex) { if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode m_boxed = new LazyInternalExceptionHolder(ex); throw; } } else { try { boxed = new Boxed((T)Activator.CreateInstance(typeof(T))); } catch (System.MissingMethodException) { Exception ex = new System.MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT")); if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode m_boxed = new LazyInternalExceptionHolder(ex); throw ex; } } return boxed;}
View Code

 

转载于:https://www.cnblogs.com/qixinbo/p/11534379.html

你可能感兴趣的文章
高校成绩管理数据库系统的设计与实现 - 实验报告
查看>>
PM(Project Manager):系列博客
查看>>
spring事务之——spring配置事务的五种方式
查看>>
delphi数组之菜鸟篇
查看>>
node
查看>>
day01 Java基础
查看>>
Web开发应该注意的问题
查看>>
异常处理
查看>>
SSH2中实例化不了Action的一个原因
查看>>
EF,MVC相关项目请参见→
查看>>
HDFS常用命令
查看>>
图像处理与分析导论
查看>>
浅谈压缩感知(二十二):压缩感知重构算法之正则化正交匹配追踪(ROMP)
查看>>
二叉搜索树的后序遍历序列
查看>>
苹果向求职者抛出的8大难题
查看>>
杂项-数学软件:Mathematica
查看>>
第3章 深入理解init
查看>>
if判断IE浏览器的类型
查看>>
PoJ1979 Red and Black (DFS)
查看>>
POJ 3009 Curling 2.0(DFS + 模拟)
查看>>