1.Lazy<T>的使用
无意间看到一段代码,在创建对象的时候使用了Lazy,顾名思义Lazy肯定是延迟加载,那么它具体是如何创建对象,什么时候创建对象了? 先看这段示列代码:
public class OrderController : Controller { private readonly Lazy<OrderService> _orderSrv = new Lazy<OrderService>(); public ActionResult CreateOrder(OrderModel model) { var result = _orderSrv.Value.CreateOrder(model); return Json(result); } }
使用非常简单,把 OrderService 放到Lazy<T> 中,然后 _orderSrv.Value 的时候才真正创建OrderService 对象。
那么问题来,是不是每次_orderSrv.Value 一下,就创建一个对象了? 想到这里,程序猿基本的条件反射,对着Lazy按下F12。
构造函数中,有个isThreadSafe的参数,默认是true。ok,参考了msdn的例子,我们来测试看看。
static void Main(string[] args) { Lazy<int> number = new Lazy<int>(() => Thread.CurrentThread.ManagedThreadId); Thread t1 = new Thread(() => Console.WriteLine("number on t1 = {0} ThreadID = {1}" ,number.Value, Thread.CurrentThread.ManagedThreadId)); t1.Start(); Thread t2 = new Thread(() => Console.WriteLine("number on t2 = {0} ThreadID = {1}" ,number.Value, Thread.CurrentThread.ManagedThreadId)); t2.Start(); Thread t3 = new Thread(() => Console.WriteLine("number on t3 = {0} ThreadID = {1}" , number.Value,Thread.CurrentThread.ManagedThreadId)); t3.Start(); Console.ReadLine(); }
结果:
很明显,number 被ID=10的线程初始化后,值一直没有改变,说明三个线程用的是同一个实例。
再试试 isThreadSafe=false 。
Lazy<int> number = new Lazy<int>(() => Thread.CurrentThread.ManagedThreadId, false);
结果(1):
number实例被ID=11的线程使用后,其他线程就不能再正确使用了,number.value=0 说明 int并没有被。
结果(2):
直接报错了,我的理解是,Lazy此时并不支持多线程并发。
2.Lazy<T> 的 valueFactory
继续在f12中找到解释:
结合我们上面的例子,可分析出,valueFactory是个委托,number.value的时候就是由valueFactory来创建这个实例。
看到这里,我们发现Lazy<T> 还是很强大的,可以用T的默认构造函数来创建实例也可以用指定的Func来创建实例,而且还支持多线程安全。
3.Lazy<T>的工作原理
用反编译插件看看代码:
static Lazy() { Lazy<T>.ALREADY_INVOKED_SENTINEL = () => default(T); Lazy<T>.PUBLICATION_ONLY_SENTINEL = new object(); }
[__DynamicallyInvokable] public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } this.m_threadSafeObj = Lazy<T>.GetObjectFromMode(mode); this.m_valueFactory = valueFactory; }
看到 default(T) 和 this.m_valueFactory = valueFactory 大概也知道是如何创建实例了吧。