【问题标题】:Does using lazy evaluation when inserting into a ConcurrentDictionary prevent the requested value from being calculated multiple times?插入 ConcurrentDictionary 时使用惰性求值是否会阻止多次计算请求的值?
【发布时间】:2020-06-11 16:10:12
【问题描述】:

在他的.NET 中的并发一书中,Riccardo Terrell 提供了两个版本的 Memoize 函数(如下),并声称第二个函数比第一个函数执行得更好,因为它避免了重复的缓存项初始化,同时还线程安全。

我的问题是 - 第二个版本是否仍然会导致重复的缓存项初始化?

对于添加到字典中的每个键/值对,一个 new 惰性实例被初始化并返回,这意味着 X 线程可能会导致 X 惰性类型被初始化,最终导致 X 调用 func(a),这正是函数的第一个版本中发生的情况?从我所见,看起来第二版的行为与第一版完全相同。

版本 1

public Func<T, R> MemoizeThreadSafe<T, R>(Func<T, R> func) where T : IComparable
{
  ConcurrentDictionary<T, R> cache = new ConcurrentDictionary<T, R>();   ①  
  return arg => cache.GetOrAdd(arg, a => func(a));
}

第 2 版

static Func<T, R> MemoizeLazyThreadSafe<T, R>(Func<T, R> func) where T : IComparable
{
    ConcurrentDictionary<T, Lazy<R>> cache = new ConcurrentDictionary<T, Lazy<R>>();   ①  
    return arg => cache.GetOrAdd(arg, a => new Lazy<R>(() => func(a))).Value;
}

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    GetOrAdd 将始终返回相同的值。两个线程可能会竞相为相同的参数值创建Lazy&lt;R&gt;,但它们最终都会引用其中的一个。他们都会依次调用Value,它会进行一次自己的初始化。只有对 Value 的调用才会强制进行此初始化,然后才会调用 func(a)

    另一个Lazy&lt;R&gt; 将被放弃并最终被GCed1,而不会被要求初始化其值。


    1假设 GC 将来会运行,那么您不会运行空 GC,等等。

    【讨论】:

    • 太好了,谢谢。我没有想到会创建两个Lazy&lt;R&gt;,但只会访问其中一个的值,因此永远不会初始化另一个中包含的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 2012-05-02
    • 1970-01-01
    • 2011-12-05
    • 2020-07-10
    • 2013-12-07
    • 2014-02-28
    相关资源
    最近更新 更多