【问题标题】:Return Property if not null in thread safe manner如果以线程安全的方式不为空,则返回属性
【发布时间】:2015-12-16 10:48:22
【问题描述】:

我的问题实际上是对this 的扩展,所以关于在返回之前测试属性是否为空的问题。我也有类似的情况:

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 问题的回答提到这不是线程安全的方法。有人可以建议为什么它不是,如果有办法以线程安全的方式做到这一点?

【问题讨论】:

  • 您给出的代码的第一个问题不是线程安全 - 这是堆栈溢出,因为您的 MyProp getter 会调用自己...

标签: c# .net multithreading thread-safety


【解决方案1】:

如果另一个线程正在运行,则该线程可以在测试和您线程上对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;
          }
        }
    }

【讨论】:

    【解决方案2】:

    谁能告诉我原因

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

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

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

    SetProp转换为返回IEnumerable&lt;T&gt;而不是设置字段,并使用Lazy&lt;T&gt;(默认情况下,初始化为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);
    }
    

    【讨论】:

    • 虽然另一个答案也不错,但我喜欢这个感觉多么干净。谢谢。
    【解决方案3】:

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

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

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

    您是说最后一行使用 _Values 作为支持字段并测试 null 而不是 MyProp?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-12
      • 1970-01-01
      • 2015-09-01
      • 2018-02-12
      • 2021-09-12
      • 2020-08-09
      • 1970-01-01
      相关资源
      最近更新 更多