stm*_*max 6 f# mailboxprocessor
当其 MailboxProcessor 被处理(或以其他方式停止)时,是否可以让 PostAndAsyncReply 立即返回?或者是否有一些关于如何安全地使用 PostAndReply 方法而不造成死锁的“模式”/最佳实践?
现在我遇到的问题是 PostAndAsyncReply 在 MailboxProcessor 被处理后永远不会返回。使用 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)
Run Code Online (Sandbox Code Playgroud)
编辑:我见过的大多数邮箱处理器示例(包括 MSDN 上的示例)甚至不介意处理邮箱处理器。并且 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
Run Code Online (Sandbox Code Playgroud)
有两件事要记住:
如果邮箱处理器正在运行一些长时间运行的工作(如上面的睡眠),则在完成后会进行处理(这是因为异步工作流的性质 - 它们不会强制取消计算)
我不得不使用StartAsTask而不是RunSynchronously在最后开始任务。出于某种原因(不太确定), usingRunSynchronously似乎不会取消计算。
您可以轻松地将其包装在MailboxProcessor内部使用的某个类中,公开类似的接口并添加此功能 - 但对于一个单一的答案来说有点太多了!
要回答您关于 的问题Dispose,我不完全确定这种行为是什么,但处理邮箱处理器会处理一个内部AutoResetEvent(请参阅源代码),用于表示消息已到达。我想这只是意味着邮箱处理器不会接受任何进一步的消息。
| 归档时间: |
|
| 查看次数: |
641 次 |
| 最近记录: |