F# 异步处置

发布于 2024-12-05 07:30:57 字数 1108 浏览 0 评论 0原文

我写了这个小的网络侦听器模拟:

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 上不调用 Dispose?

作为一个附带问题,我这样做是因为我想测试在网络服务中响应文本的正确行为是什么。我不确定我是否应该做:

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

或者

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

或者诸如此类的事情。

谢谢!

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 {
                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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

開玄 2024-12-12 07:30:57

如有疑问,请使用反射器:)。 use 关键字创建“using”的范围,直到块结束。在异步工作流程中使用时,如果对 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 最后将继续递归调用该函数,并且如果您将 use 替换为“ C# using() { } ”,其中} 括号位于 Async.ReturnFrom 之后,则永远不会调用 dispose

将 use 部分包装在 do 块中应该可以解决问题:

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

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()
            }
薯片软お妹 2024-12-12 07:30:57

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

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()
}
瞳孔里扚悲伤 2024-12-12 07:30:57

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文