.NET AppDomain.Unload会触发失控线程

时间:2013-10-10 11:08:14

标签: c# .net multithreading appdomain

我有一个.NET 3.5 SP1应用程序,它是一个Excel加载项。应用程序分为父AppDomain(Excel)和子域,我们在其中加载所有dll。当我们希望更新我们的应用程序时,我们卸载子域,替换文件并重新加载它。

不幸的是,卸载域将激活2个工作线程,并且它们将开始消耗CPU周期(20-40%)。

如果我使用VS 2010进行调试,那么在AppDomain.Unload之前和之后,除了Excel的主线程之外,没有线程处于调用堆栈的活动状态。 AppDomain.Unload确实已卸载,因为如果我再次尝试调用卸载,则会获得AppDomainUnloadedException

如果我使用ProcessExplorer,我可以看到2个线程正忙着工作,即使VS调试器已经破坏。看着callstack什么也没有显示,因为没有符号。

  • ntkrnlpa.exe + 0x6eacb
  • ntkrnlpa.exe + 0x2bfd0
  • HAL.DLL + 0x2ef2
  • ntkrnlpa.exe + 0x6a6cf
  • ntdll.dll + 0xe514
  • 的Mscorwks.dll + 0x992d
  • mscorwks.dll + 0x52568
  • mscorwks.dll + 0x15b469
  • KERNEL32.DLL + 0xb729

如果我使用WinDbg,我可以看到2个叛徒线程的callstack。它总是一样的:

  • 警告:堆栈展开信息不可用。以下帧可能是错误的。
  • NTDLL!KiFastSystemCallRet
  • mscorwks + 0x992d
  • mscorwks!InstallCustomModule + 0x1eca0
  • mscorwks!CorExitProcess + 0x503b
  • kernel32!GetModuleFileNameA + 0x1ba

我创建了一个非常简单的测试应用程序来加载/卸载子程序集。使用简单的1级装配执行此操作时,它可以正常工作。如果我让它加载/卸载真实应用程序的子域,它会触发相同的renegade线程。

创建子域的代码如下:

AppDomainSetup appSetup = new AppDomainSetup();
appSetup.ApplicationBase = baseDir;

var ps = new PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
return AppDomain.CreateDomain(name, null, appSetup, ps, null);

从父域到子域的通信是通过代理和反射进行的。创建它的代码如下:

string assName = typeof(ApplicationProxy).Assembly.FullName;
string className = typeof(ApplicationProxy).FullName;

var obj = _childDomain.CreateInstanceAndUnwrap(assName, className, false, 
    System.Reflection.BindingFlags.Default,
    null, new object[]{_sessionGuid}, 
    CultureInfo.InvariantCulture,
    null, new Evidence(AppDomain.CurrentDomain.Evidence));

_proxy = (ApplicationProxy)obj;

我已经大量搜索了这个问题,找不到任何有类似问题的人。该应用程序是10个项目大,所以我不能发布它。

我想知道是否有人遇到类似的事情并且有一些提示给我。否则,是否有人对如何解决问题有任何想法?

1 个答案:

答案 0 :(得分:6)

感谢Hans让我走上了正确的道路。

有一些带有终结器的类,所以我在每个类中加了一个断点。在其中一个中,有人调用ThreadPool.QueueUserWorkItem。工作项永远不会被调用,而是留下这两个线程(1个中止正在执行的线程,1个来完成东西)永远循环。

我在测试项目中对它进行了测试,确实如此。

孩子们,教训是不让经理编写线程代码。