簡體   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