Потокобезопасные однократные методы расчета - PullRequest
1 голос
/ 05 апреля 2009

Довольно часто мне нужно свойство в моем классе, которое нужно вычислять и кэшировать.

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

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

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

   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

РЕДАКТИРОВАТЬ:

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

Ответы [ 4 ]

2 голосов
/ 05 апреля 2009

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

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

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

2 голосов
/ 05 апреля 2009

Важно ли, чтобы никогда не рассчитывалось дважды? то есть, если два потока произошли , чтобы запросить его одновременно и вычислить значение независимо, то это остановка показа? В большинстве случаев это не так - в этом случае просто проверьте 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 голосов
/ 05 апреля 2009

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

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

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 голосов
/ 05 апреля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...