我有一个侦听连接客户端的简单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。
我想它与递归函数有关,但是不知道该如何稳定它。
问题在于您的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)