[英]Restart MailboxProcessor after failure?
我试图通过MailboxProcessor<'Msg>
类开始使用F#中的代理,我很快意识到我没有适当的处理异常的方法。 在Haskellian的世界中,不会有任何例外,因此处理问题的正确方法是简单地将其作为响应案例提供。 因此,座席可以回复以下内容:
type AgentResponse =
| HandledData of string
| InvalidData of string
然后可以调用代理的.PostAndReply
方法,并获取一个InvalidData
其中包含指示数据为何无效的消息。 但是,这不是Haskell,有时会发生异常。 因此,如果我这样做:
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
时会返回InvalidOperationException
,而处理此问题的自然方法似乎是创建一个具有纯净状态的新代理,但是所有排队的消息都会丢失。
除了在try..with
包装代理的整个主体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..
块; 目标函数正常返回时,先前的代码不会停止运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.