繁体   English   中英

真正的惰性缓存模式? F#

[英]truly lazy cache pattern? F#

我有以下类型用于实现简单的惰性缓存:

module CachedFoo =

let mutable private lastAccess:Option<DateTime> = None

// returns a lazy value that initializes the cache when
// accessed for the first time (safely)
let private createCacheInitialization() =
    lazy(
        let someObject = SomeLongRunningOperation()
        lastAccess <- Option.Some(DateTime.Now)
        someObject
    )

// current cache represented as lazy value
let mutable private currentCache = createCacheInitialization()

// Reset - cache will be re-initialized next time it is accessed
// (this doesn't actually initialize a cache - just creates a lazy value)
let MaybeReset() =
    if (lastAccess.IsSome && DateTime.Now > (lastAccess.Value + TimeSpan.FromSeconds (10.0))) then
        currentCache <- createCacheInitialization()

let GetCache() =
    MaybeReset()
    currentCache.Value

第一个问题:上面的线程安全吗? 似乎lazy()默认情况下是线程安全的,但是我想我需要对lastAccess字段的分配进行一些锁定吗?

第二个也是最重要的一点:这是懒惰的,因为直到有人要求它的值才可以检索它的值,但是,我认为我什至可以通过返回最后一个缓存的对象来更懒惰,即使在调用Reset()的情况下,但在后台启动一个异步线程来调用此方法。

在C#中将是这样的:

public SomeObject GetCache() {
    try {
        return currentCache.Value;
    } finally {
        ThreadPool.QueueUserWorkItem(new WaitCallback(MaybeReset));
    }
}

我将如何在F#中做到这一点? (如果解决方案使用花哨的异步内容而不是使用ThreadPool API,则奖励)。

我认为更新lastAccess是线程安全的,这有两个原因

  • 您只能在lazy执行此操作,这意味着无论如何它只会被更新一次(尽管使用Reset可能会有更细微的竞争,但我不确定)

  • lastAccess是(对Option )单个引用,因此无论如何都会自动进行原子更新

要启动新的“即发即弃” async来重新计算值,请执行以下操作:

let GetCache() =
    let v = currentCache.Value // to make sure we get the old one
    async { MaybeReset() } |> Async.Start
    v

感谢Ganesh的洞察力,我终于选择了这种解决方案,该解决方案不会让第二个请求者在刷新结果时等待结果:

module CachedFoo =

let mutable private lastAccess:Option<DateTime> = None

// returns a lazy value that initializes the cache when
// accessed for the first time (safely)
let private createCacheInitialization() =
    lazy(
        let someObject = SomeLongRunningOperation()
        lastAccess <- Option.Some(DateTime.Now)
        someObject
    )

// current cache represented as lazy value
let mutable private currentCache = createCacheInitialization()

let lockObject = new Object()

let timeout = TimeSpan.FromSeconds (10.0)

// Reset - cache will be re-initialized next time it is accessed
// (this doesn't actually initialize a cache - just creates a lazy value)
let MaybeReset() =
    lock lockObject (fun () ->
        if (lastAccess.IsSome && DateTime.Now > (lastAccess.Value + timeout)) then
            let newCache = createCacheInitialization()
            ignore(newCache.Force())
            currentCache <- newCache
    )

let GetCache() =
    let v = currentCache.Value // to make sure we get the old one
    async { MaybeReset() } |> Async.Start
    v

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM