У меня есть кэш данных, которые становятся обновленными от внешнего источника, и я хочу ограничить свой итог доступа его кэш (только для чтения) в моем приложении. Я не хочу иметь, обновляют источник данных каждый раз, мне нужен доступ к нему (т.е. на инстанцировании идут и вытягивают все данные, в которых я нуждаюсь, поскольку существует довольно мало данных, который совершенствуется).
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#?
Тип Ленивый
, как сказал Брайан, - хорошее место для начала. Это позволяет вам гарантировать, что вычисление будет выполняться, когда требуется значение, и гарантирует безопасность потоков, что означает, что вычисление будет выполняться только один раз (хотя в некоторых случаях вы также можете использовать 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
класс, который принимает только функцию инициализации и инкапсулирует остальной код в многократно используемый фрагмент (например, если вам нужно кэшировать несколько значений).
И .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 год, и становится все реже иметь дело с примитивами синхронизации; языки и библиотеки инкапсулируют все общие шаблоны.)