Ok, so I've been looking at the source code for Lazy<T> because I want to extend it. I know that in theory, it is supposed to be thread safe, but I don't know how it can be. Looking it's it .value getter, it does not lock on anything before reading the value.
public T Value
{
get
{
Boxed boxed = null;
if (m_boxed != null )
{
// Do a quick check up front for the fast path.
boxed = m_boxed as Boxed;
if (boxed != null)
{
return boxed.m_value;
}
LazyInternalExceptionHolder exc = m_boxed as LazyInternalExceptionHolder;
Contract.Assert(m_boxed != null);
exc.m_edi.Throw();
}
// Fall through to the slow path.
#if !FEATURE_CORECLR
// We call NOCTD to abort attempts by the debugger to funceval this property (e.g. on mouseover)
// (the debugger proxy is the correct way to look at state/value of this object)
Debugger.NotifyOfCrossThreadDependency();
#endif
return LazyInitValue();
}
}
It's my understanding that to be thread safe, something must lock when both writing as well as reading, because if a read get's interrupted by a write it can return incorrect data or even have an error. Is this understanding correct, or is something complicated happening with Lazy<T>?
答案 0 :(得分:6)
Read/writes to a variable of a reference type are atomic, so it's not possible for such a read to ever return a value that was not written to it, even with no locking. The value being read there is only ever assigned once, when the Lazy generates its value, so either the value is null
, and it moves on to the more complex logic, or it isn't, and we already have a value to return. If it moves on it does actually use locking mechanisms to ensure multiple threads aren't attempting to create the value at the same time.