.NET 4.0's System.Lazy<T> class offers three Thread-Safety modes via the enum LazyThreadSafetyMode, which I'll summarise as:
I'd like to have a lazy-initialized value which follows slightly different thread-safety rules, namely:
Only one concurrent thread will attempt to create the underlying value. On successful creation, all waiting threads will receive the same value. If an unhandled exception occurs during creation, it will be re-thrown on each waiting thread, but it will not be cached and subsequent attempts to access the underlying value will re-try the creation & may succeed.
So the key differince with LazyThreadSafetyMode.ExecutionAndPublication is that if a "first go" at creation fails, it can be re-attempted at a later time.
Is there an existing (.NET 4.0) class that offers these semantics, or will I have to roll my own? If I roll my own is there a smart way to re-use the existing Lazy<T> within the implementation to avoid explicit locking/synchronization?
N.B. For a use case, imagine that "creation" is potentially expensive and prone to intermittent error, involving e.g. getting a large chunk of data from a remote server. I wouldn't want to make multiple concurrent attempts to get the data since they'll likely all fail or all succeed. However, if they fail, I'd like to be able to retry later on.
By default, Lazy<T> objects are thread-safe. That is, if the constructor does not specify the kind of thread safety, the Lazy<T> objects it creates are thread-safe.
Lazy initialization is a technique that defers the creation of an object until the first time it is needed. In other words, initialization of the object happens only on demand. Note that the terms lazy initialization and lazy instantiation mean the same thing—they can be used interchangeably.
Thread safety is the technique which manipulates shared data structure in a manner that guarantees the safe execution of a piece of code by the multiple threads at the same time. A code is called thread-safe. If it is run concurrently without break function.
Something like this might help:
using System;
using System.Threading;
namespace ADifferentLazy
{
/// <summary>
/// Basically the same as Lazy with LazyThreadSafetyMode of ExecutionAndPublication, BUT exceptions are not cached
/// </summary>
public class LazyWithNoExceptionCaching<T>
{
private Func<T> valueFactory;
private T value = default(T);
private readonly object lockObject = new object();
private bool initialized = false;
private static readonly Func<T> ALREADY_INVOKED_SENTINEL = () => default(T);
public LazyWithNoExceptionCaching(Func<T> valueFactory)
{
this.valueFactory = valueFactory;
}
public bool IsValueCreated
{
get { return initialized; }
}
public T Value
{
get
{
//Mimic LazyInitializer.EnsureInitialized()'s double-checked locking, whilst allowing control flow to clear valueFactory on successful initialisation
if (Volatile.Read(ref initialized))
return value;
lock (lockObject)
{
if (Volatile.Read(ref initialized))
return value;
value = valueFactory();
Volatile.Write(ref initialized, true);
}
valueFactory = ALREADY_INVOKED_SENTINEL;
return value;
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With