F # MailboxProcessore perdite di memoria in Blocco Try / Catch
-
20-12-2019 - |
Domanda
Aggiornato dopo l'errore evidente sottolineato da John Palmer nei commenti.
Il seguente codice risulta in 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
.
L'errore è andato se non uso il blocco di prova / catch.
Aumentare il tempo di sospensione montare solo l'errore.
Aggiornamento 2: Immagino che il problema qui sia che la funzione smette di essere ricorsivo alla coda poiché la chiamata ricorsiva non è più l'ultima da eseguire.Sarebbe bello per qualcuno con più f # esperienze per disugarlo perché sono sicuro che questa è una situazione di perdita di memoria comune in agenti F # come il codice è molto semplice e generico.
Soluzione
Soluzione:
Si è rivelato una parte di un problema più grande: la funzione non può essere ricorsiva alla coda se la chiamata ricorsiva è effettuata all'interno del blocco Try / Catch poiché deve essere in grado di srotolare lo stack se ilL'eccezione viene lanciata e quindi deve salvare le informazioni di chiamata di chiamata.
Maggiori dettagli qui:
Codice correttamente riscritto (Try / Catch and Torna separato):
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
)
. Altri suggerimenti
I suspect the issue is actually here:
while true do
agent.Post "hello"
All the "hello"
s that you post have to be stored in memory somewhere and will be pushed much faster than the output can happen with printf
See my old post here http://vaskir.blogspot.ru/2013/02/recursion-and-trywithfinally-blocks.html
- random chars in order to satisfy this site rules *
Basically anything that is done after the return (like a try/with/finally/dispose) will prevent tail calls.
See https://blogs.msdn.microsoft.com/fsharpteam/2011/07/08/tail-calls-in-f/
There is also work underway to have the compiler warn about lack of tail recursion: https://github.com/fsharp/fslang-design/issues/82