简体   繁体   English

失败后重新启动MailboxProcessor?

[英]Restart MailboxProcessor after failure?

I'm trying to get started working with agents in F# via the MailboxProcessor<'Msg> class, and I quickly realized I had no proper handling for exceptions. 我试图通过MailboxProcessor<'Msg>类开始使用F#中的代理,我很快意识到我没有适当的处理异常的方法。 In a Haskellian world, there wouldn't be any exceptions, and so the proper way to handle issues would be to simply provide them as a response case; 在Haskellian的世界中,不会有任何例外,因此处理问题的正确方法是简单地将其作为响应案例提供。 so an agent could reply with something like: 因此,座席可以回复以下内容:

type AgentResponse =
    | HandledData of string
    | InvalidData of string

One could then call the agent's .PostAndReply method and get an InvalidData containing a message indicating why the data is invalid. 然后可以调用代理的.PostAndReply方法,并获取一个InvalidData其中包含指示数据为何无效的消息。 However, this isn't Haskell, and sometimes exceptions happen. 但是,这不是Haskell,有时会发生异常。 So if I do this: 因此,如果我这样做:

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)

The second call to agent.PostAndReply blocks indefinitely. agent.PostAndReply的第二次调用无限期阻塞。 When not using AsyncReplyChannel and therefore just calling agent.Post , the calls don't block, but once the agent encounters an exception, new messages just stay in the queue. 当不使用AsyncReplyChannel并因此仅调用agent.Post ,调用不会阻塞,但是一旦代理遇到异常,新消息就只会留在队列中。 Restarting the agent in either case appears impossible, as the agent.Start function returns an InvalidOperationException when called again, and the natural way to handle this seems to be to create a new agent with a clean state, but then all queued messages are lost. 在这两种情况下,都无法重新启动代理,因为agent.Start函数在再次InvalidOperationException时会返回InvalidOperationException ,而处理此问题的自然方法似乎是创建一个具有纯净状态的新代理,但是所有排队的消息都会丢失。

Beyond just wrapping the entire body of an agent in try..with , is there any good way to get an agent to continue running after an exception? 除了在try..with包装代理的整个主体try..with ,还有什么好方法可以让代理在发生异常后继续运行? Alternatively, has a "standard" way of dealing with this been established that someone can point me to? 或者,是否已经建立了可以指出我的“标准”处理方式?

You do have exceptions in Haskell : try Data.List.head [] in ghci.... 您在Haskell中确实有例外:尝试在ghci中使用Data.List.head []

Unfortunately, lack of dependent types means, in Haskell or in F#, that we can write type correct code which has no computational meaning. 不幸的是,在Haskell或F#中,缺少依赖类型意味着我们可以编写没有计算意义的类型正确的代码。

Practically, wrapping the with a try ... with block is not a bad idea for dealing with exception. 实际上,用try ... with块包装不是处理异常的好主意。 You dont need to wrap the entire body, just the non-pure part of your code though. 您不需要包装整个正文,而只是包装代码的非纯部分。

Then classically, you yield back a value made with the appropriate constructor. 然后,按照经典方法,您将产生一个使用适当的构造函数生成的值。

One possible option is a helper type similar to the HandlingMailbox defined by Tomas Petricek over at this question , which runs the body of the agent repeatedly: 一个可能的选项是类似于在此问题上 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

This can be started and run like a normal MailboxProcessor , but will re-run itself if the provided agent body throws an exception. 可以像正常的MailboxProcessor一样启动和运行它,但是如果提供的代理主体引发异常,它将重新运行。

Edit: Change the inner MailboxProcessor to use a recursive function versus a while true do.. block; 编辑:更改内部MailboxProcessor以使用递归函数而不是while true do..块; the previous code would not stop running when the target function returned normally. 目标函数正常返回时,先前的代码不会停止运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM