我有一个从外部来源刷新的数据缓存,我想限制我的应用程序内部的缓存(只读)。我不希望每次需要访问它时都刷新数据源(即在实例化时去拉取我需要的所有数据,因为有相当多的数据保持最新)。
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()
但是缺少null会让我陷入困境。我想我可以使用一个选项类型等,但它让我循环
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#中是否有这种语法的首选模式?
答案 0 :(得分:14)
.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年,很少需要明确处理同步原语;语言和库正在封装所有常见模式。)
答案 1 :(得分:9)
很抱歉重新提出一个旧问题,只是想指出有些人可能会尝试在公共属性中公开{{1}},在这种情况下,以下代码可能会有用:
Instance
答案 2 :(得分:7)
问题是如何实现Singleton模式,而不是如何实现Lazy-Load模式。单例可以通过多种方式安全地实现,例如:
// Standard approach in F# 2.0: using an implicit constructor.
type Singleton private() =
static let instance = new Singleton()
static member Instance = instance
// Abbreviated approach in F# 3.0: using an implicit constructor with auto property.
type Singleton private() =
static member val Instance = Singleton()
// Alternative example: When you have to use an explicit ctor,
// and also want to check instanciation upon each access of the property.
/// This type is intended for private use within Singleton only.
type private SyncRoot = class end
type Singleton =
[<DefaultValue>]
static val mutable private instance: Singleton
private new() = { }
static member Instance =
lock typeof<SyncRoot> (fun() ->
if box Singleton.instance = null then
Singleton.instance <- Singleton())
Singleton.instance
修改强>
添加了一个带有私有隐式ctor的简化F#2.0示例,带有显式ctor的示例现在使用单独的私有类型作为同步根。感谢kvb提示。
编辑2 添加了F#3.0自动属性语法。
答案 3 :(得分:4)
Brian提到的Lazy
类型是一个很好的开始。它允许您确保在需要值时运行计算并确保线程安全,这意味着计算只运行一次(尽管在某些情况下,您也可以使用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
类,并将其余代码封装到可重用的部分中(例如,如果您需要缓存多个值)。 / p>