1、使用多线程延迟创建“唯一”的对象
在实际开发中,我们可能会希望将一个对象的创建延迟到需要真正用到它的时候。最典型的是使用数据库连接对象访问数据库。在分布式的软件系统中,客户端与服务器一般不会在同一台计算机上,创建数据库对象并启动到远程数据库连接是一件比较耗废系统资源的事情。当然,通过编写一些条件判断语句我们可以实现“在需要的时候才创建对象”这一目标。
以下是一段典型代码:
class Parent { A obj = null ; public void VisitEmbedObject() { if (obj == null ) obj = new A (); } } |
然而,当延迟动态创建的对象会被多线程共享访问时,就麻烦了,想想上述代码放在线程函数中,由多个线程同时执行,如果不施加任何的同步手段, A 对象可能会被创建多个!这很容易理解,由于操作系统采用分时时间片的调度方法将 CPU 分配给特定线程执行,因此完全可能发生某个线程还没有执行完毕,另一个线程又投入运行的情况。
在本例中,有可能一个线程在创建对象过程中(而此时 obj 仍是 null ),另一个线程尝试访问 obj 会发现它仍是 null ,于是又会创建另一个 A 对象!
在过去,为了解决这个问题,一般需要给多线程共享资源加锁:
class Parent { A obj = null ; public void VisitEmbedObject() { lock ( this ) { if (obj == null ) obj = new A (); } //... } } |
这个方法是传统的编程方法。
然而,到了.NET4.0,有更简单更方便的方法达到同样的目的。
2、泛型类Lazy<T>
泛型类Lazy<T>位于System命名空间,是.NET4.0新引入的。它的功能就是解决多线程运行环境下的对象延迟创建问题。
通过实例可以很清楚地掌握它的用法(Demo:UseLazyExample)。
本例中我们定义了一个很简单的类型A:
class A { public A() { Console.WriteLine("A 对象创建,其标识: {0}",this.GetHashCode()); } public int IntValue { get; set; } } |
以下代码实现了对象的延迟创建:
class Program { static void Main(string[] args) { Lazy<A> AObj = new Lazy<A>(); Console.WriteLine(" 现在将给 A 对象的 IntValue 属性赋值 100"); A obj = AObj.Value; // 此处导致对象创建! obj.IntValue = 100; Console.WriteLine("A 对象 {0} 的 IntValue 属性 ={1}", obj.GetHashCode(),obj.IntValue); Console.ReadKey(); } } |
运行结果如下:
上述代码虽然实现了对象的延迟创建,但示例代码运行于单线程环境下,还没有显示出使用 泛型类 Lazy<T> 的好处。