C# 使用Lazy实现的ConcurrentDictionary上的更新方法<;T>;不起作用

C# 使用Lazy实现的ConcurrentDictionary上的更新方法<;T>;不起作用,c#,multithreading,concurrency,lazy-initialization,concurrentdictionary,C#,Multithreading,Concurrency,Lazy Initialization,Concurrentdictionary,正如在博客文章中所描述的,我使用了Lazy类使我的ConcurrentDictionary线程安全。我创建了一个名为LazyConcurrentDictionary的新类,它在内部存储一个ConcurrentDictionary。要更新存储在内部字典中的值,我想实现一个更新方法。如上所述,我需要指定一个键及其当前对应的值,以及一个新值,当前值应替换为该值。当然,这仅在指定的当前值等于内部字典中键表示的实际值时才起作用 当我尝试按如下方式实现更新方法时,结果发现更新没有发生: public boo

正如在博客文章中所描述的,我使用了
Lazy
类使我的
ConcurrentDictionary
线程安全。我创建了一个名为
LazyConcurrentDictionary
的新类,它在内部存储一个
ConcurrentDictionary
。要更新存储在内部字典中的值,我想实现一个更新方法。如上所述,我需要指定一个键及其当前对应的值,以及一个新值,当前值应替换为该值。当然,这仅在指定的当前值等于内部字典中键表示的实际值时才起作用

当我尝试按如下方式实现更新方法时,结果发现更新没有发生:

public bool Update(TKey key, TValue currentValue, TValue newValue)
{
    return dictionary.TryUpdate(key,
    new Lazy<TValue>(() => newValue, LazyThreadSafetyMode.ExecutionAndPublication),
    new Lazy<TValue>(() => currentValue, LazyThreadSafetyMode.ExecutionAndPublication));            
}
public bool更新(TKey-key、TValue-currentValue、TValue-newValue)
{
返回dictionary.TryUpdate(键,
新的Lazy(()=>newValue,LazyThreadSafetyMode.ExecutionAndPublication),
新的Lazy(()=>currentValue,LazyThreadSafetyMode.ExecutionAndPublication));
}
我假设这是因为两个
懒惰的
对象之间的相等比较。即使底层对象是实例化的并且相等,但是
惰性的
对象不是。我的假设正确吗


但是,我想为我的
LazycourrentDictionary
实现一个更新方法,需要某种解决方法或其他解决方案。你能想到什么吗?

不幸的是
TryUpdate
不接受
IEqualityComparer
来比较值,因此如果你想让框架使用值相等,你必须使用你自己的类型而不是框架
Lazy
(还需要意识到,比较值将解决两个懒惰实例)。幸运的是,这很容易做到

public class MyLazy<T> : Lazy<T>
{
    private IEqualityComparer<T> valuecomparer;
    public MyLazy(IEqualityComparer<T> valueComparer = null)
    {
        this.valuecomparer = valuecomparer ?? EqualityComparer<T>.Default;
    }
    public override bool Equals(object obj)
    {
        MyLazy<T> other = obj as MyLazy<T>;
        if (other == null)
            return false;
        return valuecomparer.Equals(Value, other.Value);
    }
    public override int GetHashCode()
    {
        return valuecomparer.GetHashCode(Value);
    }
}
public类MyLazy:Lazy
{
私人品质比较师;
公共MyLazy(IEqualityComparer valueComparer=null)
{
this.valuecomparer=valuecomparer??EqualityComparer.Default;
}
公共覆盖布尔等于(对象对象对象)
{
MyLazy other=对象为MyLazy;
如果(其他==null)
返回false;
返回值comparer.Equals(值,其他.Value);
}
公共覆盖int GetHashCode()
{
返回valuecomparer.GetHashCode(值);
}
}
因此,您需要在自己的并发字典实现中使用该类,而不是
Lazy


综上所述,如果性能是一个主要问题,那么最好不要将ConcurrentDictionary与Lazy结合使用,从零开始编写自己的内容,而是采用这样一种方式,即花时间确保对GetOrAdd的调用只解析参数一次,这与ConcurrentDictionary不同。如果您要创建一个新类,它会给人一种印象,即您正在使用它,可能需要它。

遗憾的是
TryUpdate
不接受
IEqualityComparer
来比较值,因此,如果希望框架使用值相等,就必须使用自己的类型,而不是框架
Lazy
(还必须意识到,比较值将解决这两个懒惰实例)。幸运的是,这很容易做到

public class MyLazy<T> : Lazy<T>
{
    private IEqualityComparer<T> valuecomparer;
    public MyLazy(IEqualityComparer<T> valueComparer = null)
    {
        this.valuecomparer = valuecomparer ?? EqualityComparer<T>.Default;
    }
    public override bool Equals(object obj)
    {
        MyLazy<T> other = obj as MyLazy<T>;
        if (other == null)
            return false;
        return valuecomparer.Equals(Value, other.Value);
    }
    public override int GetHashCode()
    {
        return valuecomparer.GetHashCode(Value);
    }
}
public类MyLazy:Lazy
{
私人品质比较师;
公共MyLazy(IEqualityComparer valueComparer=null)
{
this.valuecomparer=valuecomparer??EqualityComparer.Default;
}
公共覆盖布尔等于(对象对象对象)
{
MyLazy other=对象为MyLazy;
如果(其他==null)
返回false;
返回值comparer.Equals(值,其他.Value);
}
公共覆盖int GetHashCode()
{
返回valuecomparer.GetHashCode(值);
}
}
因此,您需要在自己的并发字典实现中使用该类,而不是
Lazy


综上所述,如果性能是一个主要问题,那么最好不要将ConcurrentDictionary与Lazy结合使用,从零开始编写自己的内容,而是采用这样一种方式,即花时间确保对GetOrAdd的调用只解析参数一次,这与ConcurrentDictionary不同。如果你要创建一个新类,它会给人一种印象,即你已经使用了足够多的类,可能需要它。

为什么你需要一个
懒惰的
?只是为了避免文章中描述的场景?在我看来,这是一种矫枉过正的做法,你可以简单地使用其他方法来更新字典,比如TryGet。类似于:
if(dictionary.TryGetValue(key,out var value)&&value==currentValue)dictionary[key]=newValue
@Fabjan好吧,本文描述的情况是计算值既昂贵又不应该重复(可能是因为它会产生副作用)。你不能总是回避它。你提出的替代方案不安全。字典可以在对基础字典执行的每个操作之间进行更改(这是
ConcurrentDictionary
的全部要点),这意味着您可能会在不等于比较值时覆盖该值,因此不符合方法的要求。你的
Update
方法对我来说没有多大意义。通常,当您希望延迟包装值的实例化时,您希望使用
Lazy
类。在您的例子中,包装的值已经实例化,因为它是作为
Update
方法的参数(
TValue newValue
)传递的。那有什么意义呢?如果使用
Lazy
的全部目的是为了避免为同一个键创建多个值,那么这个目的已经失败了。为什么需要
Lazy
?只是为了避免