2009-04-05 3 views
1

Весьма распространено, что мне нужно свойство в моем классе, которое необходимо вычислить и кэшировать.Рекомендации по оптимальному переходу на один раз

Обычно я использую замок и булево верхний чек, если он обработан или нет. Иногда я делаю это в аксессуарах.

Что производительность хит этого подхода? Есть ли лучший способ.

Пример кода моего общего подхода к этому:

Sub Main() 
     Dim X AS New X() 

     For i AS Integer = 0 To 50 
      Dim Thr AS New Threading.Thread(ADdressOF X.ProcessData) 
      Thr.Start() 
     Next 

    End Sub 

Private Class X 

    Private DataCached AS Boolean 
    Private ProcessedData AS String 
    Private Lock AS New Object() 
    Public Function ProcessData() AS String 

    Synclock Lock 
     IF NOT DataCached Then 
      DataCached = True 
      ProcessedData = DoStuff() 
     End If 
    End Synclock 

     Console.Writeline(ProcessedData)   
     Return ProcessedData 
    End Function 


    Function DoStuff() AS String 
     Threading.Thread.Sleep(1000) 
     Console.Writeline("Processed") 
     return "stuff" 
    End Function 

End Class 

EDIT:

Это то, что необходимо рассчитать, когда доступ, поскольку она постоянно меняется. Расчет конструктора здесь не помогает. (образец - действительно упрощенная версия того, что я делаю)

ответ

2

Это важно, чтобы это никогда не рассчитывается дважды? то есть, если два потока произошли с, чтобы запросить его в одно и то же время и вычислить значение независимо, то это шоу-стоппер? В большинстве случаев это не так - в этом случае, просто проверить null (так как это строка): (например, в C#, извинений):

if(processedData == null) { 
     processedData = DoStuff(); 
    } 
    return processedData; 

Все последующие вызовы должны увидеть новое значение (Я не думаю, что нам понадобится volatile, если он скрыт внутри свойства/метода).

Это имеет то преимущество, что блокировка свободной и простой.

Еще одна хитрости заключается в использовании статического свойства вложенного класса:

string SomeValue { 
    get {return MyCache.SomeValue;} 
} 
static class MyCache { 
    public static readonly string SomeValue; 
    static MyCache() { 
     SomeValue = DoStuff(); 
    } 
} 

Рассчитываются лениво, но правила статических инициализаторов означают, что он гарантированно работать только один раз (за исключение отражения).

+0

Это хороший момент, в худшем случае это будет рассчитано 3 раза. Я уверен, что не более того. –

+0

(см. Обновление для того, чтобы он никогда не был пересчитан без использования блокировок) –

+0

@ Маркс, даже если нет блокировки, я все еще предполагаю, что внутри есть замок, не так ли? –

0

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

2

Вы можете улучшить параллелизм с оптимизацией двойной проверки:

If Not DataCached Then 
    Synclock Lock 
    If Not DataCached Then 
     ProcessedData = DoStuff() 
     DataCached = True ' Set this AFTER processing 
    End If 
End Synclock 

Это позволит избежать критической секции после первого запуска.

0

Firslty, я бы переместить кэширование за пределами вашего класса, который содержит бизнес-логику и сохранить свой бизнес-логику чистой и позволяют управлять кэшированием независимо от приложения. Но это не ваш вопрос ...

Вам нужно не упомянуть, если вы будете принимать больше попадания потенциально вычислительных вещи несколько раз, пока кэш не будет жарко. Простой подход:

if (Cache["key"] == null) 
    Cache["key"] = obj.processData(); 

return Cache["key"]; 

Кэш сам должен обеспечить, чтобы это было безопасно.

Если вы хотите, чтобы явно блокировать, пока кэш заселяются, то у вас уже есть семантика, чтобы сделать это в коде выше, однако я рекомендую это изменение:

if (Cache["key"] == null) { 
    Synclock blockIfProcessing 
    if (Cache["key"] == null) 
     Cache["key"] = obj.processData(); 
    End Synclock 
} 

return Cache["key"]; 

В основном это останавливает вас блокирование каждый вызов, когда кеш горячий, и даст лучшую производительность, а также защитит вас от возможных условий гонки.

Помните, что, как только у вас есть два разных замка, вы открываете себя до потенциальных тупиков (и это далеко выходит за рамки этой темы).

Ищите решение для кэширования .Net.

+0

Теоретически это может вернуть значение null (другой поток может удалить элемент из кэша между его назначением и возвратом. Чтобы избежать этого, используйте что-то вроде temp = Cache ["key"]; if (temp == null) {temp = processData(); Cache ["key" = temp;} return temp; – Joe

Смежные вопросы