2013-07-23 38 views
3

我被困在修復使用MailboxProcessor的F#項目中運行的TFS測試。問題是我收到來自TFS測試運行器的以下警告:MailboxProcessor.Dispose不會使對象GC可收集

System.AppDomainUnloadedException:試圖訪問卸載的AppDomain。如果測試開始一個線程但沒有停止,就會發生這種情況。確保在完成之前停止由測試啓動的所有線程。

我猜這個問題是由MailboxProcessor造成的。下面的代碼演示問題(我從FSI運行它):

open System.Threading 
open System 

type TestDisposable() = 
    let cts = new CancellationTokenSource() 
    let processMessage (inbox:MailboxProcessor<int>) = 
     let rec loop n = 
      async { 
       let! msg = inbox.Receive() 
       return! loop (n+msg) 
      } 
     loop 0 

    let agent = MailboxProcessor<int>.Start (processMessage, cts.Token) 

    interface IDisposable with 
     member this.Dispose() = 
      (agent :> IDisposable).Dispose() 
      cts.Cancel() 
      cts.Dispose() 
      printfn "TestDisposable.Dispose called" 

do 
    let weakTarget = 
     use target = new TestDisposable() 
     new WeakReference (target) 

    GC.Collect() 
    GC.WaitForPendingFinalizers() 
    GC.WaitForFullGCComplete() |> ignore 
    GC.Collect() 

    printfn "WeakTarget is alive: %b" weakTarget.IsAlive 

我希望輸出線說weakTarget是。但其活着。 我認爲這表明一些內存泄漏。問題是我做錯了什麼? 第二個問題是GC問題是否與TFS測試運行器問題有關。

+0

我會說最初的錯誤可能是由於郵箱處理器可能會在郵箱處理器處理完之後啓動多餘的線程而造成的。這是一個完全自然的性能優化。 –

+0

同意。而問題是「我怎樣才能殺死郵箱處理器」? – Sly

+0

我運行了一個簡單的測試,通過運行無限循環創建新的'TestDisposable'來實際發生垃圾收集。經過500k次迭代後,我看到內存使用量沒有增加。我建議你的'GC'功能沒有做你的想法。 –

回答

3

您發佈的示例代碼將保留對target的引用,可能是因爲您擁有頂級綁定(use target = new TestDisposable())。

如果將代碼更改爲類似於下面的代碼,您將看到weakTarget已死亡,因爲對target的引用僅對test()函數是本地的。

do 
    let test() = 
     use target = new TestDisposable() 
     new WeakReference(target) 

    let weakTarget = test()  

    GC.Collect() 
    GC.WaitForPendingFinalizers() 
    GC.WaitForFullGCComplete() |> ignore 
    GC.Collect() 

    printfn "WeakTarget is alive: %b" weakTarget.IsA 

我不知道這是否能夠修復您的原始問題,因爲這對於您編寫示例代碼的方式來說是相當特殊的。

+0

是的,非常感謝,這有助於解決這兩個問題。我曾經在TestClass的默認ctor中的let綁定中創建一個測試目標,這導致了AppDomainUnloadedException的問題。現在我已經將目標初始化移動到每個TestMethods正文,並且它正常。雖然我不明白爲什麼在我的原始示例中,即使在目標達到其範圍結束後,對目標的引用仍然存在。 – Sly

+0

也許是因爲簡寫語法(完整語法是「let x = ... in ...」)隱藏了let後面的其餘部分是將let語句作爲閉包持有的事實? –