懒加载(Lazy<T>)是否可用作缓存技术?

10

我想要使用 .NETLazy<T> 类来实现线程安全的缓存。假设我们有以下设置:

class Foo
{
    Lazy<string> cachedAttribute;

    Foo()
    {
        invalidateCache();
    }

    string initCache()
    {
        string returnVal = "";
        //CALCULATE RETURNVAL HERE
        return returnVal;
    }

    public String CachedAttr
    {
        get
        {
            return cachedAttribute.Value;
        }
    }

    void invalidateCache()
    {
        cachedAttribute = new Lazy<string>(initCache, true);
    }
}

我的问题:

  1. 这个方案会起作用吗?
  2. 锁定应该如何工作?

我觉得在invalidateCache附近缺少了一个锁,但是怎么也想不出来。

我确定这里肯定有问题,只是我还没找到哪里出了问题。

[编辑]

好吧,看起来我是对的,我还有一些事情没有考虑到。如果一个线程看到过时的缓存,那就非常糟糕了,所以“懒惰”方式不够安全。然而,这个属性访问次数非常频繁,所以我希望进行过早的优化,以便学习一些东西,并且拥有一个可用于线程安全缓存的模式。我会继续努力解决。

P.S.:我决定使对象线程不安全,并仔细控制对对象的访问。

2个回答

10

嗯,在这种情况下,它不是线程安全的,因为在失效后,一个线程仍然可能看到旧值,另一个线程看到新值 - 因为第一个线程可能没有看到 cachedAttribute 的更改。理论上,这种情况可能会永久存在,尽管这种可能性非常小 :)

对于不变值的缓存,我认为使用 Lazy<T> 更好 - 这更符合其预期使用方式 - 但如果您能够应对在另一个线程中长时间使用一个旧的“失效”值的可能性,我想这也是可以的。


是的,在这种情况下,无效的值并不好(至少不是任意长的值)。谢谢。 - Chris Pfohl

3

cachedAttribute是一个共享资源,需要保护免受并发修改的影响。

使用lock来保护它:

private readonly object gate = new object();

public string CachedAttr
{
    get
    {
        Lazy<string> lazy;
        lock (gate)                         // 1. Lock
        {
            lazy = this.cachedAttribute;    // 2. Get current Lazy<string>
        }                                   // 3. Unlock
        return lazy.Value                   // 4. Get value of Lazy<string>
                                            //    outside lock
    }
}

void InvalidateCache()
{
    lock (gate)                             // 1. Lock
    {                                       // 2. Assign new Lazy<string>
        cachedAttribute = new Lazy<string>(initCache, true);
    }                                       // 3. Unlock
}

或者使用Interlocked.Exchange

void InvalidateCache()
{
    Interlocked.Exchange(ref cachedAttribute, new Lazy<string>(initCache, true));
}

volatile 在这种情况下可能也可以使用,但它会让我感到头痛。


我建议将cachedAttribute的检索放在锁内,但是让Value的评估发生在锁外部 - Jon Skeet
谢谢您的快速回答。多线程让我头疼,所以不用担心,如果您只是担心volatile,那么您和我一样。这两种情况是否会阻止读取旧值? - Chris Pfohl
@Cpfohl:不,我的回答只是防止对“cachedAttribute”进行并发修改。@Jon Skeet提出的问题仍然存在。 - dtb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接