Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 线程安全懒惰类_C#_Multithreading_Thread Safety - Fatal编程技术网

C# 线程安全懒惰类

C# 线程安全懒惰类,c#,multithreading,thread-safety,C#,Multithreading,Thread Safety,我有一个类Lazy,它惰性地计算表达式: public sealed class Lazy<T> { Func<T> getValue; T value; public Lazy(Func<T> f) { getValue = () => { lock (getValue) {

我有一个类
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();
    }
}
公共密封类
{
Func-getValue;
T值;
公共图书馆(Func f)
{
getValue=()=>
{
锁定(getValue)
{
值=f();
getValue=()=>值;
}
返回值;
};
}
公共交通警队()
{
返回getValue();
}
}
基本上,我试图避免在对对象求值后锁定对象的开销,因此我在调用时用另一个函数替换
getValue

它显然在我的测试中起作用,但我无法知道它是否会在生产中爆炸


我的班级是线程安全的吗?如果没有,那么可以做些什么来保证线程安全呢?

我不完全确定您试图用这段代码做什么,但我正在构建一种“惰性”类,自动、异步调用辅助函数并存储其值。

我不完全确定您试图用这段代码做什么,但是我创建了一种“惰性”类,它可以自动、异步地调用辅助函数并存储其值。

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

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;
    }
}
公共密封类
{
函数f;
T值;
volatile bool computed=假;
void GetValue(){lock(LockObject){value=f();computed=true;}}
公共图书馆(Func f)
{
这个。f=f;
}
公共交通警队()
{
如果(!computed)GetValue();
返回值;
}
}

您是否可以通过对实际值使用标志或保护值来完全忽略重新计算函数?即:

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;
    }
}
公共密封类
{
函数f;
T值;
volatile bool computed=假;
void GetValue(){lock(LockObject){value=f();computed=true;}}
公共图书馆(Func f)
{
这个。f=f;
}
公共交通警队()
{
如果(!computed)GetValue();
返回值;
}
}

这看起来更像一种缓存机制,而不是“惰性评估”。此外,请勿更改
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();
    }
}
公共密封类
{
Func-getValue;
T值;
对象锁定值=新对象();
公共图书馆(Func f)
{
getValue=()=>
{
锁定(锁定值)
{
值=f();
getValue=()=>值;
}
返回值;
};
}
公共交通警队()
{
返回getValue();
}
}

这看起来更像一种缓存机制,而不是“惰性评估”。此外,请勿更改
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();
    }
}
公共密封类
{
Func-getValue;
T值;
对象锁定值=新对象();
公共图书馆(Func f)
{
getValue=()=>
{
锁定(锁定值)
{
值=f();
getValue=()=>值;
}
返回值;
};
}
公共交通警队()
{
返回getValue();
}
}

您的代码有几个问题:

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

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

  • 即使在解决了上述问题之后,您也可能需要一个内存屏障,以确保只有在将新值存储到内存中之后,才会替换委托


  • 然而,我会使用来自Konrad Rudolph的flag方法(只是确保您不会忘记实现这一点所需的“volatile”)。这样,您就不需要在每次检索值时调用委托(委托调用非常快;但它们不如简单地检查bool那么快)。

    您的代码有几个问题:

  • 您需要一个对象来执行锁定。不要锁定被更改的变量-锁总是处理对象,因此如果getValue被更改,多个线程可能会进入锁定的扇区