F# Async Dispose
I wrote this little web listener simulation:
Agent.Start(fun (_ : MailboxProcessor<unit>) ->
let listener = new HttpListener()
listener.Prefixes.Add(addr)
listener.Start()
let rec respondOut() = async {
开发者_C百科 let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
return! respondOut()
}
respondOut()
)
I don't understand why Dispose is not called on disp on every loop?
As a side question, I'm doing all this because I want to test what is the proper behavior to respond text in a web service. I'm not sure if I should be doing:
use s = Context.Response.OutputStream
use sw = new StreamWriter(s)
sw.Write("test")
or
Context.Response.Write("Test")
Context.Response.End()
or whatnot.
Thanks!
When in doubt, use reflector :). The use keyword create the scope of "using" till then end of the block. When used inside the async workflow if you de-sugar the async keyword you will get something like:
Async.Bind(Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
(fun context ->
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
Async.ReturnFrom ( respondOut() )
)
Now the call Async.ReturnFrom at last will continue calling the function recursively and if you replace the use with " C# using() { } " where the } bracket is after the Async.ReturnFrom then the dispose will never get called
Wrapping the use part in a do block should solve the problem:
let rec respondOut() = async {
let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
do
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
return! respondOut()
}
use
extends to the end of the block, so I would expect Dispose
to be called after the recursive computation returns (which is never, in this case, since it loops unconditionally). If you want to dispose of the resource earlier, you'll need to delimit the scope of the use
binding somehow. Perhaps something like this would work (I haven't tried it):
let rec respondOut() = async {
let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
do! async {
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
}
return! respondOut()
}
My guess is disp
is optimized away in your compiled code since it isn't used. Try adding printfn "%A" disp
on the next line.
精彩评论