MailboxProcessor.Dispose не делает объект GC коллекционным

Я застрял с исправлением моих тестов TFS, запускаемых для проекта F#, который использует MailboxProcessor. Проблема заключается в следующем предупреждении, которое я получаю от тестировщика TFS:

System.AppDomainUnloadedException: попытка получить доступ к незагруженному домену приложения. Это может произойти, если тест (ы) запустил поток, но не остановил его. Убедитесь, что все потоки, запущенные тестом (-ами), остановлены до завершения.

Я думаю, что проблема вызвана 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

Я ожидаю, что выходная строка скажет, что уязвимый объект мертв. Но он жив. Я думаю, что это указывает на некоторую утечку памяти. Вопрос в том, что я делаю не так? И второй вопрос - связана ли проблема GC с проблемой бегуна тестов TFS.

1 ответ

Решение

Код примера, который вы разместили, будет содержать ссылку на 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

Я не знаю, исправит ли это вашу первоначальную проблему, поскольку это довольно специфично для того, как вы написали пример кода.

Другие вопросы по тегам