4

我坚持为使用 MailboxProcessor 的 F# 项目修复我的 TFS 测试运行。问题是我从 TFS 测试运行器收到的以下警告:

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 is dead。但它还活着。我认为这表明存在一些内存泄漏。问题是我做错了什么?第二个问题是GC问题是否与TFS test runner问题有关。

4

1 回答 1

4

您发布的示例代码将保留对 的引用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

我不知道这是否解决了您最初的问题,因为这与您编写示例代码的方式相当具体。

于 2013-07-23T13:25:26.553 回答