当它的邮箱处理器被处理(或以其他方式停止)时,是否可以使PostAndAsyncReply立即返回?或者是否有一些“模式”/最佳实践如何安全地使用PostAndReply方法而不会造成死锁?
现在我遇到的问题是,当邮箱处理器处理完毕后,PostAndAsyncReply永远不会返回。使用超时参数不是一个选项,因为我不能等待(除了选择合理的超时是非常困难或不可能的,因为它取决于太多因素)。
[<Test>]
let ``waiting for a reply from a disposed agent``() =
use server = MailboxProcessor.Start(fun inbox -> async {
()
})
(server :> System.IDisposable).Dispose()
server.PostAndReply (fun reply -> reply) // <- deadlock
|> ignore)
编辑:我见过的大多数使用MailboxProcessors的例子(包括MSDN上的例子)甚至不介意处理MailboxProcessors。并且MSDN没有解释邮箱处理器在处理时的反应。是否没有必要处置它们?
答案 0 :(得分:4)
没有内置支持取消待处理的PostAndReply
电话,但您可以实现此功能。要处理处理,您可以在try .. finally
中包装邮箱正文。在finally
块中,您可以以某种方式表示邮箱处理器已停止。以下使用取消令牌源:
let disposed = new System.Threading.CancellationTokenSource()
let server = MailboxProcessor<AsyncReplyChannel<obj>>.Start(fun inbox ->
async {
try
// The normal body of the mailbox processor goes here
do! Async.Sleep(1000)
printfn "done"
finally
// Cancel all pending calls post and reply
disposed.Cancel()
})
// Dispose the mailbox processor
(server :> System.IDisposable).Dispose()
// When sending message, we use 'StartAsTask' and set a cancellation token,
// so that the work is stopped when the mailbox processor finishes
let wait = server.PostAndAsyncReply(fun reply -> reply)
let task = Async.StartAsTask(wait, cancellationToken = disposed.Token)
printfn "%A" task.Result
要记住两件事:
如果邮箱处理器正在运行一些长时间运行的工作(比如在上面休眠),则在完成此操作后将进行处理(这是因为异步工作流的性质 - 它们没有强制取消计算)
我不得不使用StartAsTask
代替RunSynchronously
来结束任务。出于某种原因(不太确定),使用RunSynchronously
似乎不会取消计算。
你可以轻松地将它包含在内部使用MailboxProcessor
的某个类中,公开类似的接口并添加此功能 - 但这对于单个答案来说有点太多了!
要回答有关Dispose
的问题,我不完全确定其行为是什么,但处置邮箱处理器会释放内部AutoResetEvent
(请参阅the source code)表示消息已到达。我想这只是意味着邮箱处理器不会接受任何进一步的消息。