gpt4 book ai didi

f# - 接收前的MailboxProcessor : Memory leak using return!

转载 作者:行者123 更新时间:2023-12-03 19:05:13 26 4
gpt4 key购买 nike

给定以下代理,这是一种简单的缓存机制:

type CacheMsg<'a,'b> = Add of 'a * 'b | ForceFlush

type CacheAgent<'a, 'b when 'a : comparison>(size:int, flushCont:Map<'a, 'b> -> unit) =
let agent = MailboxProcessor.Start(fun inbox ->
let rec loop (cache : Map<'a, 'b>) = async {
let inline flush() =
flushCont cache
loop Map.empty

if cache.Count > size then return! flush()

let! msg = inbox.Receive()

match msg with
| Add (key, value) ->
if cache.ContainsKey key then
return! loop cache
else return! loop (cache.Add(key, value))
| ForceFlush -> return! flush() }
loop Map.empty)

member x.AddIfNotExists key value = Add(key,value) |> agent.Post
member x.ForceFlush() = agent.Post ForceFlush

该代理将继续占用内存(似乎在调用 flushCont时似乎没有释放内存)。

给定相同的代码,但有少许更改:
type CacheMsg<'a,'b> = Add of 'a * 'b | ForceFlush

type CacheAgent<'a, 'b when 'a : comparison>(size:int, flushCont:Map<'a, 'b> -> unit) =
let agent = MailboxProcessor.Start(fun inbox ->
let rec loop (cache : Map<'a, 'b>) = async {
let inline flush() =
flushCont cache
loop Map.empty

let! msg = inbox.Receive()

match msg with
| Add (key, value) ->
if cache.ContainsKey key then
return! loop cache
else
let newCache = cache.Add(key, value)
if newCache.Count > size then
return! flush()
else return! loop (cache.Add(key, value))
| ForceFlush -> return! flush() }
loop Map.empty)

member x.AddIfNotExists key value = Add(key,value) |> agent.Post
member x.ForceFlush() = agent.Post ForceFlush

我已将决定何时刷新的表达式移到联合用例 Add中。这导致内存按预期方式被释放。

第一种方法有什么问题,因为它会泄漏内存?

最佳答案

第一个版本不是tail recursive

它不是尾递归,因为此表达式不是函数中的最后一个表达式:

if cache.Count > size then return! flush()

在表达之后,您调用
let! msg = inbox.Receive()

因此 flush()调用不是最后发生的事情。在 flush中隐式进行的递归调用完成后,执行将需要返回到下一个表达式,您在其中调用 inbox.Receive()。这意味着上下文必须将先前的调用保留在堆栈中,因为递归不在尾端:还有更多工作要做。

在第二个示例中,对 flushloop的所有调用都位于尾部位置。

如果您来自C#背景,则可能会认为 return! flush()退出了该函数,但实际情况并非如此。唯一的原因
if cache.Count > size then return! flush()

甚至没有对应的 else分支的编译也都是 because the expression returns unit 。这意味着 then分支内的代码并没有真正退出该功能-它只是在该分支中执行工作(在本例中为 flush()),然后继续执行后续表达式。

关于f# - 接收前的MailboxProcessor : Memory leak using return!,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23771835/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com