由于线程没有停止,因此在测试中出现System.AppDomainUnloadedException

时间:2016-08-04 22:08:31

标签: .net multithreading f# task-parallel-library task

我有一台服务器启动TcpListener以侦听指定端口上的传入连接。它实现如下:

type TCPListenerServer(discoveryPort:int) =
    let server = new TcpListener (IPAddress.Loopback, discoveryPort)

    let activeConnections = new List<TcpClient>()
    let cancellationToken = new System.Threading.CancellationTokenSource()

    let rec loop (pendingConnection:Task<TcpClient>) = async {            
        let newPendingConnection, client =
            match pendingConnection.Status with
            | TaskStatus.Created | TaskStatus.WaitingForActivation | TaskStatus.WaitingToRun 
            | TaskStatus.WaitingForChildrenToComplete | TaskStatus.Running  ->
                (None, None)
            | TaskStatus.Faulted ->
                let result = pendingConnection.Exception
                raise (new System.NotImplementedException())
            | TaskStatus.Canceled ->
                raise (new System.NotImplementedException())
            | TaskStatus.RanToCompletion ->
                let connectionTask = server.AcceptTcpClientAsync ()
                (Some connectionTask, Some pendingConnection.Result)
            | _ -> 
                raise (new System.NotImplementedException())

        // Add the new client to the list
        Option.iter (fun c -> activeConnections.Add c) client

        // Switch the new pending connection if there is one
        let connectionAttempt = defaultArg newPendingConnection pendingConnection

        // Check that the connections are still alive
        Seq.iter (fun (connection:TcpClient) -> if not connection.Connected then activeConnections.Remove connection |> ignore) activeConnections

        Async.Sleep 1000 |> Async.RunSynchronously
        return! loop connectionAttempt
    }

    member x.Start () =
        try
            server.Start ()
            let connectionTask = server.AcceptTcpClientAsync ()
            Async.Start (loop connectionTask, cancellationToken.Token)
        with ex ->
            server.Stop ()

    member x.Stop () =
        cancellationToken.Cancel ()
        server.Stop ()

    member x.ActiveConnections =
        activeConnections

我正在做一个非常简单的测试,启动服务器并确保不会抛出异常,如下所示:

let cleanupTest (serverUsed:TCPListenerServer) =
    serverUsed.Stop ()

[<TestMethod>]
[<TestCategory(Networking)>]
member x.``Start Server`` () =
    let server = new TCPListenerServer(44000)
    server.Start ()
    Async.Sleep 5000 |> Async.RunSynchronously

    cleanupTest server

测试通过,但我在测试结果中得到以下错误:

  

System.AppDomainUnloadedException:尝试访问已卸载的   AppDomain中。如果测试开始一个线程但没有开始,则会发生这种情况   停下来。确保测试启动的所有线程都是   在完成之前停止。

回顾我的代码时,我意识到我的递归循环从未实际完成,所以我认为这可能是原因。我的解决方案是在我停止服务器时添加取消令牌(这是我已粘贴的代码,最初没有在那里)我打电话给Cancel()

不幸的是,这还没有解决问题。什么可能导致这种异常(这不会导致我的测试失败)?

1 个答案:

答案 0 :(得分:0)

原来我需要等到取消实际完成。我通过在调用cancellationToken.Cancel ()后睡2秒来完成此操作,因为我无法访问为执行以下操作而创建的初始任务:

task.Wait ()