Как реализовать Шаблон "одиночка" (синтаксис)

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

type MySingleton = 

        [<DefaultValue>]
        static val mutable private instance: MySingleton

        static member GetInstance() = 
            instance

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

if instance is null
    synchronize
    if instance is null
        instance = new MySingleton()

но отсутствие пустого указателя бросает меня для цикла. Я думаю, что могу использовать тип опции и т.д., но он бросает меня для цикла

type MySingleton = 

        [<DefaultValue>]
        static val mutable private instance: MySingleton option

        static member GetInstance() = 
            match instance with
                 | Some(i) -> i
                 | None -> 
                            *MySingleton.instance = new MySingleton()
                            MySingleton.instance*

та логика является неправильной согласно компилятору...

       if Helper.notExists MySingleton.instance then
            MySingleton.instance <- Some(new MySingleton())        
       MySingleton.instance 

я должен использовать операторы IF вместо этого? Существует ли предпочтенный шаблон для этого синтаксиса в f#?

9
задан tshepang 26 April 2014 в 09:21
поделиться

2 ответа

Тип Ленивый , как сказал Брайан, - хорошее место для начала. Это позволяет вам гарантировать, что вычисление будет выполняться, когда требуется значение, и гарантирует безопасность потоков, что означает, что вычисление будет выполняться только один раз (хотя в некоторых случаях вы также можете использовать PublicationOnly option , чтобы указать, что несколько потоков могут начать инициализацию кеша и будет использоваться только первый результат).

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

module Cache = 
  // returns a lazy value that initializes the cache when 
  // accessed for the first time (safely)
  let private createCacheInitialization() = 
    lazy( // some code to calculate cache 
          cache )
  // current cache represented as lazy value
  let mutable private currentCache = createCacheInitialization()

  // Returns the current cache
  let GetCache() = currentCache.Value
  // Reset - cache will be re-initialized next time it is accessed
  // (this doesn't actually initialize a cache - just creates a lazy value)
  let Reset() = currentCache <- createCacheInitialization()

Конечно, вы можете превратить этот код в Cache класс, который принимает только функцию инициализации и инкапсулирует остальной код в многократно используемый фрагмент (например, если вам нужно кэшировать несколько значений).

4
ответ дан 4 December 2019 в 06:49
поделиться

И .NET 4.0, и F # имеют Lazy , поэтому я думаю, вы хотите

module MySingleton =
    let private x = Lazy.Create(fun() -> 42)
    let GetInstance() = x.Value

(где 42 может быть new WhateverType () или что-то еще дорогая инициализация).

http://msdn.microsoft.com/en-us/library/dd997286.aspx

(Комментарий: 2010 год, и становится все реже иметь дело с примитивами синхронизации; языки и библиотеки инкапсулируют все общие шаблоны.)

14
ответ дан 4 December 2019 в 06:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: