代码之家  ›  专栏  ›  技术社区  ›  Juliet

线程安全懒惰类

  •  1
  • Juliet  · 技术社区  · 16 年前

    Lazy 它惰性地计算表达式:

    public sealed class Lazy<T>
    {
        Func<T> getValue;
        T value;
    
        public Lazy(Func<T> f)
        {
            getValue = () =>
                {
                    lock (getValue)
                    {
                        value = f();
                        getValue = () => value;
                    }
                    return value;
                };
        }
    
        public T Force()
        {
            return getValue();
        }
    }
    

    基本上,我试图避免在评估对象之后锁定对象的开销,所以我替换 getValue 调用时使用另一个函数。

    信息技术 显然地 在我的测试中有效,但我无法知道它是否会在生产中爆炸。

    4 回复  |  直到 16 年前
        1
  •  2
  •   Daniel    16 年前

    您的代码有几个问题:

    1. 您需要一个对象来执行锁定。不要锁定已更改的变量-锁定总是处理对象,因此如果getValue更改,多个线程可能会同时进入锁定部分。

    2. 如果多个线程正在等待锁定,则所有线程都将依次计算函数f()。您必须在锁内部检查函数是否尚未计算。

        2
  •  2
  •   Konrad Rudolph    16 年前

    难道你不能通过使用标志或保护值作为实际值来完全忽略重新评估函数吗?即。:

    public sealed class Lazy<T>
    {
        Func<T> f;
        T value;
        volatile bool computed = false;
        void GetValue() { lock(LockObject) { value = f();  computed = true; } }
    
        public Lazy(Func<T> f)
        {
            this.f = f;
        }
    
        public T Force()
        {
            if (!computed) GetValue();
            return value;
        }
    }
    
        3
  •  0
  •   Adam Maras    16 年前

    我不完全确定你想用这段代码做什么,但我 just published an article on The Code Project 构建一种“惰性”类,自动异步调用辅助函数并存储其值。

        4
  •  0
  •   Adam Robinson    16 年前

    lock

    现在的等待在很多情况下都会起作用,但是如果有两个不同的线程,请尝试按以下顺序计算表达式:

    Thread 1
    Thread 2
    Thread 1 completes
    

    线程2永远不会完成,因为线程1将释放一个与用于获取锁不同的引用上的锁(更准确地说,他将释放一个不存在的锁,因为新创建的引用从一开始就从未被锁定),并且 松开原来的锁,锁紧螺纹2。

    虽然我不完全确定这会做什么(除了执行表达式的同步计算和结果的缓存),但这应该会使它更安全:

    public sealed class Lazy<T>
    {
        Func<T> getValue;
        T value;
        object lockValue = new object();
    
        public Lazy(Func<T> f)
        {
            getValue = () =>
                {
                    lock (lockValue)
                    {
                        value = f();
                        getValue = () => value;
                    }
                    return value;
                };
        }
    
        public T Force()
        {
            return getValue();
        }
    }