RA>Большое спасибо за пример. Основную идею я понял. В посте ниже справедливо указали, на то, что моя реализация не отвечает заявленным критериям, а именно при возникновении ошибки при получении значения ожидающие потоки не получат тот же объект исключения. Сейчас набросал небольшой тест кейс и думаю как модифицировать мою реализацию. Пока безуспешно.
Как идея: можно возвращать Task. Тогда проброс ошибок будет автоматическим.
public class Lazy<T>
{
private Task<T> m_Value;
private readonly Func<Task<T>> m_Factory;
public Lazy(Func<Task<T>> factory) { m_Factory = factory; }
public Task<T> GetValueAsync()
{
// Запускаем инициализацию
Interlocked.CompareExchange(ref m_Value, m_Factory(), null);
var task = Volatile.Read(ref m_Value);
// Ошибка инициализации? Сбросим текущую таску в null, чтобы перезапуститься при след. обращении
task.ContinueWith(_ =>
Interlocked.CompareExchange(ref m_Value, null, task),
TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
return task;
}
}
RA>На сколько понимаю это связано из-за ограничения методов Volatile.* Это можно как то обойти?
Да, Volatile работает с очень ограниченным набором типов.
Ну можно использовать вложенный класс, типа такого:
public class Lazy<T>
{
private ValueHolder m_ValueHolder;
private Func<T> m_Factory;
private class ValueHolder
{
public T Value;
}
private T Value
{
get
{
Interlocked.CompareExchange(ref m_ValueHolder, new ValueHolder {Value = m_Factory()}, null);
var holder = Volatile.Read(ref m_ValueHolder);
return holder.Value;
}
}
}