Core源码(三) Lazy<T>
Lazy<T>解决什么问题?
1、大对象加载
考虑下面的需求,有个对象很大,创建耗时,并且要在托管堆上分配一大块空间。我们当然希望,用到它的时候再去创建。也就是延迟加载,等到真正需要它的时候,才去加载。
显然,这里需要加一个中间层,将大对象封装起来,暴露接口,开始并不创建大对象,等到用户真正访问对象的时候,再去创建。另外,这个中间层应该可以封装不同类型的大对象,因此需要类模版。Lazy<T>就是为了解决这个问题。
典型的使用
public Lazy<AccountService> AccountServ = new Lazy<AccountService>();
public Lazy<ProductService> ProductService = new Lazy<ProductService>();
2、将委托或者方法对象保存,并在需要的时候调用。
private readonly Lazy<IDbConnection> _connectionLazy;
public CallHistoryRepository(ConnectionFactory connectionFactory)
{
_connectionLazy = new Lazy<IDbConnection>(()=>connectionFactory.Connection);
}
一旦使用.Vale,那么对应的变量就会被实例化,IsValueCreated属性也就变成了true。
实现自己的Lazy<T>
在.NET Framework 4.0之前,大对象就是存在的,那么对于一个大型系统而言,怎么样对付一个大对象呢。主要有两点:延迟加载和即时清理。前者解决创建问题,后者解决回收问题。
那么在来看Lazy<T>的.NET Framework实现之前,我们先来自己实现一个简单的Lazy<T>吧。
class MyLazy<T> 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介绍如下
http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode%28VS.100%29.aspx
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 exception
private object m_boxed;
如果m_boxed有值,就直接装箱返回对应值(这里就要注意值类型装箱的性能损失了)。这个装箱是为了检验下m_boxed的值,因为其中有可能是异常。
二、LazyInitValue方法
如果m_boxed为空,就调用LazyInitValue方法,这里有针对线程安全模式的判断
/// <summary>
/// local helper method to initialize the value
/// </summary>
/// <returns>The inititialized T value</returns>
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;
}
三、CreateValue方法
返回一个实例对象T,采用传入的func方法,这里会对是否已经返回做出判断,如果已经返回,就返回null
/// <summary>Creates an instance of T using m_valueFactory in case its not null or use reflection to create a new T()</summary>
/// <returns>An instance of Boxed.</returns>
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<T> 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;
}
Core源码(三) Lazy<T>的更多相关文章
- 一起来看CORE源码(一) ConcurrentDictionary
先贴源码地址 https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Col ...
- 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器
1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...
- ASP.NET Core[源码分析篇] - Authentication认证
原文:ASP.NET Core[源码分析篇] - Authentication认证 追本溯源,从使用开始 首先看一下我们通常是如何使用微软自带的认证,一般在Startup里面配置我们所需的依赖认证服务 ...
- ASP.NET Core源码学习(一)Hosting
ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...
- asp.net core源码地址
https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet 这个下面是asp.net core 框架 ...
- ASP.NET Core[源码分析篇] - WebHost
_configureServicesDelegates的承接 在[ASP.NET Core[源码分析篇] - Startup]这篇文章中,我们得知了目前为止(UseStartup),所有的动作都是在_ ...
- ASP .NET CORE 源码地址
ASP .NET CORE 源码地址:https://github.com/dotnet/ 下拉可以查找相应的源码信息, 例如:查找 ASP .NET CORE Microsoft.Extension ...
- DOTNET CORE源码分析之IOC容器结果获取内容补充
补充一下ServiceProvider的内容 可能上一篇文章DOTNET CORE源码分析之IServiceProvider.ServiceProvider.IServiceProviderEngin ...
- AQS源码三视-JUC系列
AQS源码三视-JUC系列 前两篇文章介绍了AQS的核心同步机制,使用CHL同步队列实现线程等待和唤醒,一个int值记录资源量.为上层各式各样的同步器实现画好了模版,像已经介绍到的ReentrantL ...
随机推荐
- [译]Vulkan教程(25)描述符布局和buffer
[译]Vulkan教程(25)描述符布局和buffer Descriptor layout and buffer 描述符布局和buffer Introduction 入门 We're now able ...
- Vue小练习 03
""" 1.有以下广告数据(实际数据命名可以略做调整) ad_data = { tv: [ {img: 'img/tv/tv1.jpg', title: 'tv1'}, ...
- cocos2d-x 新工程的把玩
创建了cocos的工程以及初步了解了工程的结构之后,可以尝试自己改改代码了 游戏窗口的设置 首先是AppDelegate,找到AppDelegate.cpp中AppDelegate::applicat ...
- (转)vscode实现markdown流程图
原文:https://blog.csdn.net/LaySwift/article/details/79458947 1,vscode原生支持markdown,导出需要插件,基于node.js,需要n ...
- 池化HttpClient,拿去就能用
import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.HttpResp ...
- JS基础语法---作用域
作用域:使用范围 全局变量: 声明的变量是使用var声明的, 那么这个变量就是全局变量 全局变量可以在页面的任何位置使用 除了函数以外, 其他的任何位置定义的变量都是全局变量 局部变量:在函数内部定义 ...
- iOS常用算法之两个有序数组合并, 要求时间复杂度为0(n)
思路: 常规思路: 先将一个数组作为合并后的数组, 然后遍历第二个数组的每项元素, 一一对比, 直到找到合适的, 就插入进去; 简单思路: 设置数组C, 对比A和B数组的首项元素, 找到最小的, 就放 ...
- 【分享】nginx负载均衡全套视频教程
1.课件 百度网盘链接:https://pan.baidu.com/s/1On2oONVZmPwI9yIDekgRiA 提取码:c4fw 2.教程列表 3.教程下载 3.1.直接在线学习 ...
- Ubuntu下doxygen+graphviz使用概录
关键词:doxygen.Doxyfile.doxywizard.dot.graphviz等等. 使用doxygen从源码注释生成帮助文档或者SDK,输出格式有多种比如htmp.Latex等等. 如果想 ...
- June 16th, 2019. Week 25th, Sunday.
I can fly higher than an eagle, for you are the wind beneath my wings. 我之所以能飞得比鹰还高,是因为有你做我羽翼下的风. You ...