如果不为null,则以线程安全的方式返回属性

本文关键字:安全 方式 返回 属性 线程 null 如果不 | 更新日期: 2023-09-27 18:27:09

我的问题实际上是这个SO问题的扩展,关于在返回属性之前测试属性为null。我有类似的情况:

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;
        }
    }

链接SO问题的公认答案提到,这不是一种线程安全的方法。有人能告诉为什么不是,以及是否有方法以线程安全的方式做到这一点吗?

如果不为null,则以线程安全的方式返回属性

如果另一个线程正在运行,则该线程可以在测试和调用线程上的SetProp()之间调用SetProp()

我使用这样的代码,使它更安全:

   // 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;
          }
        }
    }

有人能建议的原因吗

想象一下,有两个线程,它们并行执行get_MyProp。然后就有可能得到这个序列:

  • T1:_MyProp == null->true
  • T2:_MyProp == null->true
  • T1:this.SetProp();->_MyProp初始化
  • T2:this.SetProp();->T2重写由T1计算的_MyProp值

如果有一种方法可以以线程安全的方式做到这一点,

SetProp转换为返回IEnumerable<T>而不是设置字段,并使用Lazy<T>(默认情况下,初始化将是线程安全的):

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);
}

我认为您发布的代码中可能有错误。碎片

    public IEnumerable<T> MyProp
    {
        get
        {
            if(MyProp == null)
            { // ...

是无限递归的,会导致堆栈溢出(未大写!)。

你是指最后一行使用_Values作为支持字段并测试其为null而不是MyProp吗?