NUnit异步测试导致AppDomainUnloadedException [英] NUnit async test causing AppDomainUnloadedException

查看:101
本文介绍了NUnit异步测试导致AppDomainUnloadedException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有异步操作的.NET 4.5 WCF服务.我有集成测试,该测试使用NetNamedPipeBinding构造服务主机,并通过客户端进行操作.

I have a .NET 4.5 WCF service with async operations. I have integration tests which constructs the service host using NetNamedPipeBinding and hits the operation via a client.

但是,每次这样的测试总是会导致NUnit报告以下内容:

However, each test like this always causes NUnit to report the following:

System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. 
This can happen if the test(s) started a thread but did not stop it. 
Make sure that all the threads started by the test(s) are stopped before completion.

一切对我来说都不错.谁能看到是什么原因造成的?我在GitHub上有完整的代码示例: https://github.com/devlife/codesamples

Everything looks ok to me. Can anyone see what might be causing this? I have a complete code sample on GitHub: https://github.com/devlife/codesamples

推荐答案

我遇到了同样的问题.看起来问题似乎是WCF已用于处理异步IO的宽大"完成端口线程(在ThreadPool中).

I'm having the same problem. It looks as if the issue are "lenient" completion port threads (in the ThreadPool) that have been used by WCF to handle async IO.

使用ServiceHost.Close()时,它将向所有发出工作信号的线程发出信号,但它们不会立即消失,也就是说,它们可能会超出ServiceHost.Close()操作的结束时间.因此,由于测试运行的结束,关机"过程与NUnit引发的实际AppDomain卸载竞争.

When ServiceHost.Close() is used, it will signal all those threads that work is done, but they won't go away immediately, that is, they may outlive the end of the ServiceHost.Close() operation. Thus, the "shutdown" procedure races with the actual AppDomain unloading induced by NUnit due to the end of the test run.

基本上,一个简单的Thread.Sleep(<a couple of seconds>)ServiceHost.Close()之后可以修复"此问题:-)

Basically, a simple Thread.Sleep(<a couple of seconds>) after a ServiceHost.Close() "fixes" this :-)

在互联网上进行了大量搜索之后,我找不到一个可靠的解决方案(对于某些类似问题,并非都是由于相同的原因,谷歌单元测试appdomainunloadedexception"),缺少一些解决方案抑制此警告本身的方法.

After much searching around on the internet I couldn't find a robust solution for this issue (for a selection of similar issues, not all due to the same cause though, google "unit test appdomainunloadedexception"), short of having some way to suppress this warning itself.

我尝试了不同的绑定和传输(包括 NullTransport ),但无济于事.

I tried different bindings and transports (includind the NullTransport), but to no avail.

最后,我解决了这个解决方案":

In the end I settled with this "solution":

static void PreventPrematureAppDomainUnloadHack()
{
    //
    // When NUnit unloads the test AppDomain, the WCF started IO completion port threads might
    // not have exited yet.
    // That leads to AppDomainUnloadedExceptions being raised after all is said and done.
    // While native NUnit, ReSharper oder TestDriven.NET runners don't show these, VSTest (and
    // TFS-Build) does. Resulting in very annoying noise in the form of build/test warnings.
    //
    // The following code _attempts_ to wait for all completion port threads to end. This is not
    // an exact thing one can do, however we mitigate the risk of going wrong by several factors:
    // (1) This code is only used during Unit-Tests and not for production code.
    // (2) It is only called when the AppDomain in question is about to go away anway.
    //     So the risk of someone starting new IO threads while we're waiting is very
    //     low.
    // (3) Finally, we have a timeout in place so that we don't wait forever if something
    //     goes wrong.
    //
    if (AppDomain.CurrentDomain.FriendlyName.StartsWith("test-domain-", StringComparison.Ordinal))
    {
        Console.WriteLine("AppDomainUnloadHack: enabled (use DbgView.exe for details).");
        Trace.WriteLine(string.Format("AppDomainUnloadHack: enabled for domain '{0}'.", AppDomain.CurrentDomain.FriendlyName));

        AppDomain.CurrentDomain.DomainUnload += (sender, args) =>
        {
            int activeIo;
            var sw = Stopwatch.StartNew();
            var timeout = TimeSpan.FromSeconds(3);

            do
            {
                if (sw.Elapsed > timeout)
                {
                    Trace.WriteLine("AppDomainUnloadHack: timeout waiting for threads to complete.");
                    sw.Stop();
                    break;
                }

                Thread.Sleep(5);

                int maxWorkers;
                int availWorkers;
                int maxIo;
                int availIo;
                ThreadPool.GetMaxThreads(out maxWorkers, out maxIo);
                ThreadPool.GetAvailableThreads(out availWorkers, out availIo);
                activeIo = maxIo - availIo;

                Trace.WriteLine(string.Format("AppDomainUnloadHack: active completion port threads: {0}", activeIo));

            } while (activeIo > 0);

            Trace.WriteLine(string.Format("AppDomainUnloadHack: complete after {0}", sw.Elapsed));
        };
    }
}

3秒的超时是完全任意的,每次重试之间等待5毫秒也是如此.有时我确实会收到超时",但大多数情况下都可以.

The timeout of 3 seconds is totally arbitrary and so is the wait of 5ms between each retry. Sometimes I do get a "timeout", but most of the time it works.

我确保为每个测试程序集调用一次此代码(即通过引用类型的静态ctor).

I make sure that this code is called once for every test assembly (i.e. through a static ctor of a referenced type).

在这种情况下,通常是YMMV.

As usual in such cases YMMV.

这篇关于NUnit异步测试导致AppDomainUnloadedException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆