2011-09-15 65 views
2

我写了这个小Web侦听器模拟:F#异步处置

Agent.Start(fun (_ : MailboxProcessor<unit>) -> 
     let listener = new HttpListener() 
     listener.Prefixes.Add(addr) 
     listener.Start() 

     let rec respondOut() = async { 
       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() 
    ) 

我不明白为什么处置不上DISP呼吁每一个循环?

作为一个侧面问题,我正在做这一切,因为我想测试什么是正确的行为来响应Web服务中的文本。我不知道我是否应该做的:

use s = Context.Response.OutputStream 
use sw = new StreamWriter(s) 
    sw.Write("test") 

Context.Response.Write("Test") 
Context.Response.End() 

或诸如此类的东西。

谢谢!

回答

5

如果有疑问,请使用反射器:)。 use关键字创建“使用”的范围,直到块的结尾。当异步工作流程内使用,如果你去糖async关键字,你会得到这样的:

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()) 
       ) 

现在终于通话Async.ReturnFrom将继续调用该函数递归,如果你换成“C#中使用使用(){}“,其中}托架是Async.ReturnFrom那么处置将永远不会被调用

结束语在做块使用部分应解决的问题后:

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() 
      } 
0

我的猜测是disp在编译后的代码中被优化掉了,因为它没有被使用。尝试在下一行添加printfn "%A" disp

+0

删除它,并把在wr上使用并不能解决这个问题,这就是我为什么要这样做的原因听众的麻烦。 –

+0

我很难过,但你尝试过使用'而不是? – Daniel

+0

是的,它工作正常,但这是因为它放置在匿名函数的末尾。解决了我目前的问题,但让我绝对担心使用绑定不会在异步工作流中处理。我想也许是因为递归调用没有被尾部优化,导致了Tomas曾经提到过的一种内存泄漏。 –

2

use延伸到块结束,所以我希望Dispose后递归运算回报被称为(这是从来没有,在这种情况下,因为它无条件地循环)。如果您想更早地处理资源,则需要以某种方式限定use绑定的范围。也许这样的事情会起作用(我还没有尝试过):

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() 
} 
+0

我不认为需要内部'async'。你可以将'use'块封装在parens中,以便清楚地说明处理点:'(使用disp(*用disp *做某事))''。 – Daniel

+0

@Daniel - 你不能在计算表达式中使用任意表达式,但是你可以使用'do(use ...)'而不是'do! async {use ...}'。 – kvb

+0

糟糕,忘了。 – Daniel