F # MailboxProcessor утечка памяти в блок попробовать / ловить
-
20-12-2019 - |
Вопрос
Следующий код приводит к генеракодичеству:
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
.
Ошибка исчезла, если я не использую блок TRY / CALL.
Увеличение времени сна только отложить ошибку.
Решение
<Сильное> Решение:
оказалось частью большей задачи: функция не может быть рекурсивным хвостом, если рекурсивный вызов сделан в блоке Thrue / Catch, так как он должен быть в состоянии развернуть стек, еслиИсключение брошено и, таким образом, необходимо сохранить информацию стека вызовов.
<Сильные> Подробнее Здесь:
Хвостовое рекурсирование и исключения в F #
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
)
. Другие советы
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