[英]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.