[英]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.