简单的rec函数中的F#和内存泄漏

时间:2019-07-15 13:42:17

标签: f#

我有一个简单的TCP服务器,可以监听连接客户端,而且看起来很简单

let Listener (ip:IPAddress) (port:int32) =
    async {    
        let listener = TcpListener(ip, port)
        listener.Start()   
        _logger.Info(sprintf "Server binded to IP: %A - Port: %i" ip port)

        let rec listenerPending (listener : TcpListener) = 
            async {
                if not (listener.Pending()) then return! listenerPending listener // MEMMORY LEAK SOURCE
                printfn "Test"
             }

        listenerPending listener |> Async.Start
    }

看起来很简单,但是我有一个内存泄漏问题:当它等待连接时,它会像糖果一样吞噬RAM。

我想它与递归函数有关,但不知道该怎么做才能稳定它。

1 个答案:

答案 0 :(得分:6)

问题是您的listenerPending函数不是尾部递归的。这有点违反直觉-但是return!关键字不同于中断当前执行的命令式“ return”,而是对其他可以尾部递归的函数的调用(与{{1 }},如果位置正确,则永远不会。

为说明起见,请考虑以下内容:

do!

这实际上打印了从0到10的数字!这是因为let rec demo n = async { if n > 0 then return! demo (n-1) printfn "%d" n } demo 10 |> Async.RunSynchronously 之后的代码在递归调用完成后仍会执行。您的代码结构类似,除了循环永远不会终止(至少不够快)之外。您可以通过删除return!之后的代码(或将其移至return!分支)来解决此问题。

此外,值得注意的是,您的代码并不是真正的异步-您没有任何非阻塞等待。您可能应该这样做而不是循环:

else