2014-01-12 44 views
0

我有以下類型實現一個簡單的懶惰高速緩存:真正的懶惰緩存模式? 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字段的賦值處加上一些鎖定。

其次,最重要的是:這是它的值沒有檢索到它的人需要的感覺懶散,不過,我想我甚至可以做到這一點比較懶惰通過即使在復位的情況下返回最後緩存的對象()被調用,但在後臺啓動一個異步線程來調用這個方法。

在C#中它會是這樣的:

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

我會怎麼做,在F#? (如果解決方案使用花哨的異步的東西而不是使用ThreadPool API,獎勵點)。

+0

什麼是對超時的理由 - 它似乎並沒有對內存節省給出當有人居然說他們想要的價值,它只是觸發?緩存的結果能否以某種方式變陳舊? –

+0

是的,他們可以,但如果我能找到一種方法來編碼在F#這將簡單地減少超時,以解釋「陳舊」概率 – knocte

回答

0

由於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 
1

我想更新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 
+0

謝謝,但我想你打算稱爲Reset()和不createCacheInitialization? – knocte

+0

我正在複製你的C#所做的事情,但後來我發現只是調用'createCacheInitialization'而不做任何事情,所以我更新了我的代碼以寫入'currentCache'。當然你可以調用'Reset'。 –

+0

哦,那是我的C#代碼中的一個錯字!如果您不介意,我會更新您的答案,謝謝! – knocte