Can(should?)Lazy <t>可以用作缓存技术吗?</t>

时间:2010-12-03 17:57:29

标签: c# multithreading caching locking lazy-loading

我想使用.NET的{​​{1}}类来实现线程安全缓存。假设我们有以下设置:

Lazy<T>

我的问题是:

  1. 这会起作用吗?
  2. 锁定如何工作?
  3. 我觉得我在invalidateCache附近错过了一个锁,但是对于我的生活,我无法弄清楚它是什么。

    我确定这个地方有问题,我只是没弄明白。

    [编辑]

    好吧,看起来我是对的,有些东西我没想过。如果一个线程看到一个过时的缓存,那将是一件非常糟糕的事情,所以看起来“懒惰”不够安全。虽然这个属性被访问了很多,所以我正在进行预成熟优化,希望我能学到一些东西并且有一个模式可以在将来用于线程安全的缓存。我会继续努力。

    P.S。:我决定让对象线程不安全,并且可以小心地控制对象的访问权。

2 个答案:

答案 0 :(得分:9)

嗯,它不是线程安全的,因为一个线程在失效后另一个线程看到新值后仍然可以看到旧值 - 因为第一个线程可能没有看到对cachedAttribute的更改。从理论上讲,这种情况可能永远存在,尽管这种情况不太可能:)

使用Lazy<T>作为不变值的缓存对我来说似乎更好 - 更符合预期 - 但是如果你能应对使用在另一个线程中任意长时间段的旧“无效”值,我认为这没关系。

答案 1 :(得分: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在这种情况下可能也会起作用,但它让我头疼。