【发布时间】:2015-01-02 15:35:25
【问题描述】:
我正在尝试通过 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,处理这个问题的自然方法似乎是创建一个具有干净状态的新代理,但是所有排队的消息都是丢了。
除了将代理的整个主体包裹在try..with 中之外,还有什么好方法可以让代理在异常后继续运行?或者,是否已经建立了一种“标准”的方式来处理这个问题,有人可以指点我吗?
【问题讨论】:
-
我认为
MailboxProcessor(代理)的整个想法是它会因失败而死亡。也许您可以使用另一个代理作为队列,然后在出现错误时使用TryPostAndReply队列代理启动一个新代理?如果您需要高性能,我认为try...catch可能是最好的选择。 -
@mydogisbox 仅仅捕捉一切似乎不是一个最佳的想法,但我同意这似乎也是最好的方法。我希望避免的问题是必须保留排队消息的两份副本;例如,如果我在 F# 中编写了一个邮件程序守护程序。守护进程不应该仅仅因为代理遇到我没想到的网络异常而“丢失”排队的电子邮件,因此它需要将消息保留在其他地方以及队列中。如果我需要所述队列中所有内容的第二个副本,感觉就像它破坏了代理拥有队列的目的。
-
我同意。我想知道 erlang 代理模型是如何处理这个问题的。
标签: asynchronous f# mailboxprocessor