2013-08-19 34 views
3

是否有可能使PostAndAsyncReply在其郵箱處理器處置(或以其他方式停止)時立即返回?或者有沒有一些「模式」/最佳做法如何安全地使用PostAndReply方法而不會造成死鎖?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並未解釋MailboxProcessors在處置時如何反應。不需要處理它們嗎?

回答

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開始的T最後問。出於某種原因(不太確定),使用RunSynchronously似乎不能取消計算。

你可以很容易地在使用MailboxProcessor內部一些類包裝這,暴露出類似的界面,並增加了這一功能 - 但是這是一個有點吃不消了一個答案!

要回答你關於Dispose的問題,我不能完全肯定的行爲是什麼,但配置郵箱處理器處置內部AutoResetEvent(見the source code)用於信號消息已到達。我想這僅僅意味着郵箱處理器不會接受任何進一步的消息。