已释放的MailboxProcessor上的PostAndReply



当其MailboxProcessor被释放(或以其他方式停止)时,是否可以使PostAndAsyncReply立即返回?或者,关于如何安全地使用PostAndReply方法而不产生死锁,是否存在一些"模式"/最佳实践?

现在我遇到的问题是,当MailboxProcessor被释放时,PostAndAsyncReply永远不会返回。使用timeout参数不是一个选项,因为我不能等待(此外,选择合理的超时是非常困难或不可能的,因为这取决于太多因素)。

  [<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)

edit:我看到的大多数MailboxProcessors的例子(包括MSDN上的例子)甚至都不介意处理MailboxProcessor。MSDN没有解释MailboxProcessors在被处理时的反应。没有必要处理它们吗?

没有内置的对取消挂起的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(请参阅源代码),用于表示消息已到达。我想这只是意味着邮箱处理器将不接受任何进一步的消息。

最新更新