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.

È stato utile?

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:

ricorsione e eccezioni in F #

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top