Saturday, January 23, 2010

.NET 4.0 Lazy<T> Class

.NET 4.0 and Visual Studio 2010 is raring to go. Current release date is somewhere in May 2010.

One of the new features of new framework is the Lazy<T> class which supports lazy initialization of objects.

As I wrote in some of my previous blogs I'm not best friend of (wrong used) Lazy Loading. However, there are some use cases where this is a very powerful feature. Therefore I use the phrase "Lazy Initialization" instead of "Lazy Loading" in below samples for Lazy<T> class. "Lazy Loading" usually implies automatic loading of reference data in a Data Access Layer. When I speak about "Lazy Initialization" this implies a lazy initialization of some (rare) object properties which require many or unmanaged system resources.

The following sample class shows a common case where lazy initialization becomes a very useful and often required implementation.
sealed class Product1 : IProduct {
   // -------------------------------------------------------------------
   public Product1(string name, Stream picture) {
      Name = name;
      Picture = picture;
   }
   // -------------------------------------------------------------------
   public string Name { get; set; }
   public Stream Picture { get; set; }
}
The "Picture" property of "Product" class is usually very rare needed in a whole system and it works with (probably) unmanaged resources which might cause a very high memory allocation. Always to initialize the picture of a product which might only be required in a web front-end or a sales application would cause a huge I/O traffic which is not required in system directions like billing or call-center order management.

Lazy<T> Class

The Lazy<T> class represents a wrapper class to handle those kind of property initialization.

As a quote from MSDN:
Although you can write your own code to perform lazy initialization, we recommend that you use Lazy instead. Lazy and its related types also support thread-safety and provide a consistent exception propagation policy.
If a system already works with its custom implemented lazy initialization, I'd advice to stay with this. If lazy initialization is a new requirement, we shouldn't run into evil Not Invented Here software project anti-pattern ;-) and use the existing implementation.

Here's a list of methods and constructors provided by Lazy<T> class (descriptions from MSDN):
  • ctor(valueFactory Func<T>): Initializes a new instance of the Lazy(T) class. When lazy initialization occurs, the specified initialization function is used.
  • ctor(valueFactory Func<T>, isThreadSafe bool): Initializes a new instance of the Lazy(T) class. When lazy initialization occurs, the specified initialization function and initialization mode are used.
  • Value: Gets a value that indicates whether lazy initialization has occurred for this Lazy(T) instance.
  • IsValueCreated: Gets the lazily initialized value of the current Lazy(T) instance.
Lazy<T> class provides some more methods and constructors, but the above seem to be the most important in my opinion.

As a side note for isThreadSafe parameter, a second quote from MSDN:
Some Lazy constructor overloads have a Boolean parameter named isThreadSafe that is used to specify whether the Value() property will be accessed from multiple threads. If you intend to access the property from just one thread, pass in false to obtain a modest performance benefit. If you intend to access the property from multiple threads, pass in true to instruct the Lazy instance to correctly handle race conditions in which one thread throws an exception at initialization time. If you use a constructor that does not have the isThreadSafe parameter, the value defaults to true.
If an application never works with multiple threads, isThreadSafe parameter with value false causes a faster execution due to no thread locking overhead.

How to Use

First, the "Product" class needs to be modified to support lazy initialization. I moved the stream creation into an external factory class
// ======================================================================
sealed class Product2 : IProduct {
   // -------------------------------------------------------------------
   public Product2(string name, string fileName) {
      Name = name;
      _picture = new Lazy<Stream>(() => PictureFactory.GetPicture(fileName), true);
   }
   // -------------------------------------------------------------------
   public string Name { get; set; }
   public Stream Picture {
      get { return _picture.Value; }
      set { _picture = new Lazy<Stream>(() => value, true); }
   }
   Lazy<Stream> _picture;
}
// ======================================================================
static class PictureFactory {
   // -------------------------------------------------------------------
   // get picture from disc
   public static Stream GetPicture(string fileName) {
      Console.WriteLine("## Lazy Initializing Image ##");
      if (string.IsNullOrEmpty(fileName))
         fileName = @"C:\Temp\not_available.jpg";
      return new FileStream(fileName, FileMode.Open, FileAccess.Read);
   }
}
Instead of a direct member field mapping of "Picture" property, "Product" class now works with a Lazy<T> wrapper which only initializes resources when it is requested. This implementation uses the isThreadSafe parameter with value true since we will reuse it below in a multi-threaded sample.

The "fileName" parameter in constructor and factory class to initialize the picture stream is just an example. If no file name was provided, the factory returns the stream of a "not_available.jpg" as default picture (see Special Case pattern).

Notice the Console.WriteLine within the factory class. This will show when data become initialized.

The following sample creates some products and simulates a rendering of product pictures into console.
// ======================================================================
class Program {
   // -------------------------------------------------------------------
   static void Main(string[] args) {
      Console.WriteLine("Creating products");
      List<IProduct> products = new List<IProduct>{
         new Product2("Lord of the Rings", null),
         new Product2("Avatar", @"C:\Temp\avatar.jpg"),
      };

      Console.WriteLine("Loop through products and print picture");
      products.ForEach(p => ConsolePictureRenderer.PrintPicture(p));
      products.ForEach(p => ConsolePictureRenderer.PrintPicture(p));
      Console.ReadKey();
   }
}
// ======================================================================
static class ConsolePictureRenderer {
   // -------------------------------------------------------------------
   public static void PrintPicture(IProduct product) {
      Console.WriteLine();
      // simulate picture rendering by a simple console output
      Console.WriteLine("Picture of {0}", product.Name);
   }
}

As shown in console output, picture data become initialized when it becomes accessed. In addition, picture factory is called only once for each instance of a product.



Multi Threaded

As written above, Lazy<T> class represents a thread-safe implementation of a lazy initialization class. That means, picture factory class does not need to implement a custom thread locking. It's automatically handled in Lazy<T> class.

As a last snippet, we reuse our "Product", "ProductFactory" and "ConsolePictureRenderer" classes from previous sample and change "Main" method to access the product pictures in a multi-threaded environment.
// ======================================================================
class Program {
   // -------------------------------------------------------------------
   static void Main(string[] args) {
      Console.WriteLine("Creating products");
      List<IProduct> products = new List<IProduct>{
         new Product2("Lord of the Rings", null),
         new Product2("Avatar", @"C:\Temp\test.jpg"),
      };

      Console.WriteLine("Loop through products and print picture");
      for (int i = 0; i < 4; i++) {
         ThreadPool.QueueUserWorkItem(foo =>
            products.ForEach(p =>
               ConsolePictureRenderer.PrintPicture(p)));
      }
      Console.ReadKey();
   }
}

Now, products and their pictures become accessed by four parallel threads. A second look into console output shows, all pictures are still loaded only once, since "Product" class initializes Lazy<T> class with isThreadSafe parameter with values true.



Conclusion

.NET 4.0 Lazy<T> class is a nice helper when working with large and/or unmanaged resources. Especially its thread-safe implementation increases the usability in a todays multi-processor environment.

4 comments:

  1. There is no call to Product2.Picture property in Program class.

    ReplyDelete
  2. Great content.
    Would be fantastic if you got your articles proof read for grammar. Cheers.

    ReplyDelete
  3. Thanks for the nice feedback and sorry for the grammar. I'm trying to get better with every new post.

    ReplyDelete
  4. How can the GetPicture(string fileName) method be called when there is no call to the property "Picture" in your example? Additionally your output shows "## Lazy Loading Image ''" instead of your code "## Lazy Initializing Image ##".

    ReplyDelete