Reputation: 6648
My question is really an extension to this SO question about testing a property is null before returning it. I have a similar situation:
public class MyClass
{
private readonly string _Name { get; set; }
private readonly IEnumerable<T> _Values { get; set; }
private IEnumerable<T> _MyProp { get; private set; }
public IEnumerable<T> MyProp
{
get
{
if(_MyProp == null)
{
this.SetProp();
}
return this._MyProp;
}
private set;
}
public MyClass(string Name, IEnumerable<T> Values)
{
this._Name = Name;
this._Values = Values;
}
private void SetProp()
{
// Business logic using Name and Values
this._MyProp = resultOfLogic;
}
}
The accepted answer to the linked SO question mentions that this is not a thread safe way of doing it. Can someone advise why it isn't and if there's a way to do this in a thread safe manner?
Upvotes: 2
Views: 288
Reputation: 883
I think there may be an error in the code you posted. The fragment
public IEnumerable<T> MyProp
{
get
{
if(MyProp == null)
{ // ...
is infinitely recursive and will cause a stack overflow (uncapitalised!).
Did you mean the last line to use _Values as a backing field and test that for null instead of MyProp?
Upvotes: 0
Reputation: 52518
If another thread is running, this thread can call SetProp()
between the test and the call to SetProp()
on your thread.
I use code like this, to make it more safe:
// Dedicated object to lock for this property only
private object myPropSync = new object();
private T _myPropVal;
public IEnumerable<T> MyProp
{
get
{
// Check if property is null
if(_myPropVal== null)
{
// If null -> make sure you are the only one in the next section
lock (myPropSync) {
// Re-test, because another thread can
// set the property while waiting for the lock
if (_myPropVal== null) {
this.SetProp();
}
}
}
return this._myPropVal;
}
private set {
lock (_myPropSync) {
_myPropVal = value;
}
}
}
Upvotes: 4
Reputation: 37760
Can someone advise why
Imagine, that there are two threads, which execute get_MyProp
in parallel.
Then it is possible to get this sequence:
_MyProp == null
-> true_MyProp == null
-> truethis.SetProp();
-> _MyProp is initializedthis.SetProp();
-> T2 rewrites _MyProp value, that was calculated by T1if there's a way to do this in a thread safe manner
Convert SetProp
to return IEnumerable<T>
instead of setting field, and use Lazy<T>
(by default, initialization will be thread-safe):
private IEnumerable<T> CalcProp()
{
// Business logic using Name and Values
return resultOfLogic;
}
public IEnumerable<T> MyProp
{
get { return _MyProp.Value; }
}
private readonly Lazy<IEnumerable<T>> _MyProp;
public MyClass(string Name, IEnumerable<T> Values)
{
this._Name = Name;
this._Values = Values;
this._MyProp = new Lazy<IEnumerable<T>>(CalcProp);
}
Upvotes: 2