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

Woj*_*icz 2 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
    }
Run Code Online (Sandbox Code Playgroud)

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

我想它与递归函数有关,但是不知道该如何稳定它。

Tom*_*cek 6

问题在于您的listenerPending函数不是尾递归的。这有点违反直觉-但是return!关键字不像命令式“ return”那样中断当前执行,而是调用其他一些可以尾部递归的函数(与之相反do!,永远不会)正确的位置。

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

let rec demo n = 
  async {
    if n > 0 then return! demo (n-1)
    printfn "%d" n
  }

demo 10 |> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)

实际上这会打印从0到10的数字!这是因为return!递归调用完成后,仍将执行后面的代码。您的代码结构类似,除了循环永远不会终止(至少不够快)之外。您可以通过删除代码return!(或将其移动到else分支)来解决此问题。

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

let! socket = listener.AcceptSocketAsync () |> Async.AwaitTask 
Run Code Online (Sandbox Code Playgroud)