简体   繁体   English

PostAndReply在已处置的邮箱处理器上

[英]PostAndReply on a disposed MailboxProcessor

Is it possible to make PostAndAsyncReply return immediately when its MailboxProcessor is disposed (or otherwise stopped)? 是否有可能使PostAndAsyncReply在其邮箱处理器处理(或以其他方式停止)时立即返回? Or is there some "pattern" / best practice on how to safely use PostAndReply methods without creating deadlocks? 或者是否有一些“模式”/最佳实践如何安全地使用PostAndReply方法而不会造成死锁?

Right now I have the problem that PostAndAsyncReply never returns when the MailboxProcessor has been disposed. 现在我遇到的问题是,当邮箱处理器被处理掉后,PostAndAsyncReply永远不会返回。 Using the timeout parameter isn't an option since I cannot wait (besides that choosing reasonable timeouts is quite hard or impossible anyway since it depends on way too many factors). 使用超时参数不是一个选项,因为我不能等待(除了选择合理的超时是相当困难或不可能的,因为它取决于太多的因素)。

  [<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: Most examples with MailboxProcessors I have seen (incl. the ones on MSDN) don't even mind disposing the MailboxProcessors. 编辑:我见过的大多数MailboxProcessors示例(包括MSDN上的那些)甚至不介意处置MailboxProcessors。 And the MSDN doesn't explain how MailboxProcessors react when being disposed. 并且MSDN没有解释邮箱处理器在处理时的反应。 Is it unnecessary to dispose them? 是否没有必要处置它们?

There is no built-in support for the cancellation of pending PostAndReply calls, but you can implement this. 取消暂挂的PostAndReply调用没有内置支持,但您可以实现此功能。 To handle for disposal, you can wrap the body of the mailbox in try .. finally . 要处理处理,你可以在try .. finally包装邮箱的主体。 In the finally block, you can somehow signal that the mailbox processor has stopped. finally块中,您可以以某种方式表示邮箱处理器已停止。 The following uses cancellation token source for this: 以下使用取消令牌源:

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

There are two things to keep in mind: 要记住两件事:

  • If the mailbox processor is running some long-running work (like sleeping above), then the disposal will happen after this is completed (this is because of the nature of async workflows - they do not forcefully cancel computations) 如果邮箱处理器正在运行一些长时间运行的工作(比如在上面休眠),那么在完成此操作后将进行处理(这是因为异步工作流的性质 - 它们没有强制取消计算)

  • I had to use StartAsTask instead of RunSynchronously to start the task at the end. 我必须使用StartAsTask而不是RunSynchronously来在最后启动任务。 For some reason (not quite sure), using RunSynchronously does not seem to cancel the computation. 由于某种原因(不太确定),使用RunSynchronously似乎不会取消计算。

You could easily wrap this in some class that uses MailboxProcessor internally, exposes similar interface and adds this functionality - but that's a bit too much for a single answer! 您可以轻松地将其包装在内部使用MailboxProcessor某个类中,公开类似的界面并添加此功能 - 但这对于单个答案来说有点太多了!

To answer your question regarding Dispose , I'm not entirely sure what the behaviour is, but disposing the mailbox processor disposes an internal AutoResetEvent (see the source code ) that is used to signal that a message has arrived. 要回答有关Dispose的问题,我不完全确定其行为是什么,但处置邮箱处理器会释放一个内部AutoResetEvent (请参阅源代码 ),用于表示邮件已到达。 I suppose this just means that the mailbox processor will not accept any further messages. 我想这只是意味着邮箱处理器不会接受任何进一步的消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM