Может ли (должен?) Lazy <T>использоваться в качестве метода кэширования? - PullRequest
8 голосов
/ 03 декабря 2010

Я бы хотел использовать класс .NET Lazy<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, но я не могу понять, что это такое.

Я уверен, что естьпроблема с этим где-то, я просто не выяснил, где.

[РЕДАКТИРОВАТЬ]

Хорошо, похоже, я был прав, были вещи, которые у меня не былоне думал о.Если поток видит устаревший кеш, это будет очень плохо, поэтому похоже, что «Lazy» недостаточно безопасен.Однако к этому свойству обращаются очень часто, поэтому я занимался оптимизацией до наступления зрелости в надежде на то, что смогу чему-то научиться и иметь шаблон, который можно использовать в будущем для поточно-ориентированного кэширования.Я продолжу над этим работать.

PS: Я решил сделать объект небезопасным, и вместо этого иметь доступ к объекту, который будет тщательно контролироваться.

Ответы [ 2 ]

9 голосов
/ 03 декабря 2010

Ну, это не потокобезопасно, поскольку один поток все еще может видеть старое значение после того, как другой поток увидит новое значение после аннулирования - потому что первый поток мог не видеть изменения в cachedAttribute.Теоретически, такая ситуация может сохраниться навсегда, хотя это довольно маловероятно:)

Использование Lazy<T> в качестве кэша неизменяемых значений кажется мне лучшей идеей - больше в соответствии сэто было задумано - но если вы можете справиться с возможностью использования старого «недействительного» значения для произвольно длительного периода в другом потоке, я думаю, что это будет хорошо.

3 голосов
/ 03 декабря 2010

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 может также сработать в этом сценарии, но от этого у меня болит голова.

...