Thread-Safe Initialization with LazyInitializer
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:
// 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:
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
.
Do you have a question or a suggestion about this post? Contact me!