0

在 John Palmer 在评论中指出明显错误后更新。

以下代码导致OutOfMemoryException

let agent = MailboxProcessor<string>.Start(fun agent ->

    let maxLength = 1000

    let rec loop (state: string list) i = async {
        let! msg = agent.Receive()

        try        
            printfn "received message: %s, iteration: %i, length: %i" msg i state.Length
            let newState = state |> Seq.truncate maxLength |> Seq.toList
            return! loop (msg::newState) (i+1)
        with
        | ex -> 
            printfn "%A" ex
            return! loop state (i+1)
    }

    loop [] 0
)

let greeting = "hello"

while true do
    agent.Post greeting
    System.Threading.Thread.Sleep(1) // avoid piling up greetings before they are output

如果我不使用 try/catch 块,错误就消失了。

增加睡眠时间只会推迟错误。

更新 2:我想这里的问题是函数停止尾递归,因为递归调用不再是最后一个执行的调用。对于具有更多 F# 经验的人来说,将其脱糖会很好,因为我确信这是 F# 代理中常见的内存泄漏情况,因为代码非常简单和通用。

4

4 回答 4

7

解决方案:

结果证明这是一个更大问题的一部分:如果在 try/catch 块中进行递归调用,则函数不能是尾递归的,因为如果抛出异常,它必须能够展开堆栈,因此有保存调用堆栈信息。

更多细节在这里:

F# 中的尾递归和异常

正确重写代码(单独的 try/catch 和 return):

let agent = MailboxProcessor<string>.Start(fun agent ->

    let maxLength = 1000

    let rec loop (state: string list) i = async {
        let! msg = agent.Receive()

        let newState = 
            try        
                printfn "received message: %s, iteration: %i, length: %i" msg i state.Length
                let truncatedState = state |> Seq.truncate maxLength |> Seq.toList
                msg::truncatedState
            with
            | ex -> 
                printfn "%A" ex
                state

        return! loop newState (i+1)
    }

    loop [] 0
)
于 2014-06-03T08:12:19.387 回答
2

我怀疑问题实际上在这里:

while true do
    agent.Post "hello"

您发布的所有"hello"s 都必须存储在内存中的某个位置,并且推送速度比输出速度快得多printf

于 2014-06-03T06:36:26.863 回答
2

在这里查看我的旧帖子http://vaskir.blogspot.ru/2013/02/recursion-and-trywithfinally-blocks.html

  • 随机字符以满足本网站规则 *
于 2014-06-03T13:30:04.200 回答
0

基本上在返回之后所做的任何事情(比如 try/with/finally/dispose)都会阻止尾调用。

请参阅https://blogs.msdn.microsoft.com/fsharpteam/2011/07/08/tail-calls-in-f/

还有一些工作正在进行中,让编译器警告缺少尾递归:https ://github.com/fsharp/fslang-design/issues/82

于 2018-05-17T08:56:24.883 回答