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();
}
}
您的代码有几个问题:
然而,我会使用来自Konrad Rudolph的flag方法(只是确保您不会忘记实现这一点所需的“volatile”)。这样,您就不需要在每次检索值时调用委托(委托调用非常快;但它们不如简单地检查bool那么快)。您的代码有几个问题: