Thread-Safe Initialization with LazyInitializer

 
 
  • Gérald Barré

While Lazy<T> is the go-to solution for lazy initialization in .NET, there's a lesser-known alternative that can be more efficient in certain scenarios: LazyInitializer.EnsureInitialized.

This static method provides thread-safe initialization with some key characteristics:

  • Reference types only: Only works with classes, not value types
  • Null-based detection: Uses null to determine if initialization is needed
  • Memory efficient compared to System.Lazy<T>: No additional wrapper object required

The method offers several overloads for different scenarios:

C#
// Default constructor initialization
var instance = LazyInitializer.EnsureInitialized(ref _instance);

// Custom factory method
var instance = LazyInitializer.EnsureInitialized(ref _instance, () => new Sample());

// Value factory is called once even if multiple threads needs to initialize the value
var syncLock = new object();
var instance = LazyInitializer.EnsureInitialized(ref _instance, ref syncLock, () => new Sample());

#Thread Safety Considerations

By default, LazyInitializer.EnsureInitialized behaves like LazyThreadSafetyMode.PublicationOnly:

  • Multiple threads may create instances simultaneously
  • Only the first successful assignment is kept
  • Other instances are discarded

For stricter thread safety, use the overload with a synchronization lock to ensure the factory method runs only once.

#Performance Benefits

The main advantage over Lazy<T> is reduced memory overhead. Instead of storing a Lazy<T> wrapper object, you directly store the target instance, saving one field per lazy-initialized value.

Under the hood, the implementation is similar to:

C#
var instance = Volatile.Read(ref _instance) ??
    Interlocked.CompareExchange(ref _instance, new Sample(), null);

#Tooling Support

Meziantou.Analyzer includes rule MA0173 that suggests replacing manual Interlocked.CompareExchange patterns with LazyInitializer.EnsureInitialized.

Meziantou.Analyzer suggesting to use LazyInitializer.EnsureInitializedMeziantou.Analyzer suggesting to use LazyInitializer.EnsureInitialized

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub