失败后重新启动邮箱处理器



我正在尝试通过 MailboxProcessor<'Msg> 类开始使用 F# 中的代理,但我很快意识到我没有正确处理异常。在哈斯克利的世界里,不会有任何例外,因此处理问题的正确方法是简单地将它们作为响应案例提供;因此,代理可以回复如下内容:

type AgentResponse =
    | HandledData of string
    | InvalidData of string

然后可以调用代理的 .PostAndReply 方法,并获取一个包含消息的InvalidData,指示数据无效的原因。然而,这不是哈斯克尔,有时会发生例外。因此,如果我这样做:

let agent =
    new MailboxProcessor<string * AsyncReplyChannel<AgentResponse>>(fun inbox ->
        async {
            while true do
                let! (msg, replyChannel) = inbox.Receive()
                if msg = "die" then failwith "Unknown exception encountered!"
                else replyChannel.Reply(HandledData(msg))
        })
agent.Start()
agent.PostAndReply (fun rc -> "die", rc)
agent.PostAndReply (fun rc -> "Test", rc)

agent.PostAndReply的第二次调用无限期地阻止。当不使用AsyncReplyChannel因此只调用agent.Post时,调用不会阻塞,但是一旦代理遇到异常,新消息就会保留在队列中。在任何一种情况下重新启动代理似乎都是不可能的,因为 agent.Start 函数在再次调用时返回一个InvalidOperationException,并且处理此问题的自然方法似乎是创建一个具有干净状态的新代理,但随后所有排队的消息都将丢失。

除了将代理的整个身体包裹在try..with中之外,还有什么好方法可以让代理在异常后继续运行?或者,是否建立了有人可以指出我的"标准"处理方式?

你在 Haskell 中确实有例外:尝试在 ghci 中Data.List.head []....

不幸的是,缺少依赖类型意味着,在Haskell或F#中,我们可以编写没有计算意义的类型正确的代码。

实际上,用 try ... with 块包装 对于处理异常并不是一个坏主意。你不需要包装整个主体,只需要包裹代码的非纯部分。

然后,通常,您返回使用适当的构造函数生成的值。

一个可能的选项是类似于 Tomas Petricek 在这个问题上定义的HandlingMailbox的帮助程序类型,它反复运行代理的主体:

type ResilientMailbox<'T> private(f:ResilientMailbox<'T> -> Async<unit>) as self =
    let event = Event<_>()
    let inbox = new MailboxProcessor<_>(fun _inbox ->
        let rec loop() = async {
            try
                return! f self
            with e ->
                event.Trigger(e)
                return! loop()
            }
        loop())
    member __.OnError = event.Publish
    member __.Start() = inbox.Start()
    member __.Receive() = inbox.Receive()
    member __.Post(v:'T) = inbox.Post(v)
    static member Start(f) =
        let mbox = new ResilientMailbox<_>(f)
        mbox.Start()
        mbox

这可以像普通MailboxProcessor一样启动和运行,但如果提供的代理机构抛出异常,则会重新运行。

编辑:更改内部MailboxProcessor以使用递归函数而不是while true do..块;当目标函数正常返回时,以前的代码不会停止运行。

相关内容

  • 没有找到相关文章

最新更新