终结器线程的范围是什么 - 每个应用程序域或每个进程?

时间:2008-10-27 22:04:27

标签: .net multithreading process appdomain finalizer

基于我的所有阅读,应该有一个GC线程来调用所有终结器。现在,问题是这个“一个”线程的范围是什么 - 每个进程或每个应用程序域,因为域的整个意图是在一个进程空间中分离并创建“独立”的不同应用程序。

我看了here

  

如果a中发生未处理的异常   终结CLR的执行线程   将吞下异常,对待   终结者好像完成正常,   将其从可释放队列中删除   并转到下一个条目。

     但是,更严重的是,会发生什么   如果您的终结器没有退出   某种原因,例如它阻塞,   等待永远不会的条件   发生。 在这种情况下终结者   线程将挂起,所以不再   可终结的对象将是垃圾   收集。你应该非常   意识到这种情况并坚持下去   编写最简单的代码来释放你的   终结者中的非托管资源。

     

另一个考虑因素是会发生什么   应用程序关闭期间当。。。的时候   程序关闭,垃圾收集器   将尽力打电话给终结者   所有可终结的对象,但有   某些限制:

     
      
  • 不会提升可终结对象   到了更高的堆世代   关机。

  •   
  • 任何个人终结者都会有   最多执行2秒;如果它   需要更长时间才会被杀死。

  •   
  • 最多40秒   所有终结者将被执行;如果有的话   终结者仍在执行,或   在这一点上等待整个   过程突然被杀掉。

  •   

太多帖子(甚至是官方文档)滥用术语“应用程序”,“进程”和“应用程序域” - 他们中的大多数甚至假设它们是相同的,因为通常应用程序在单个应用程序域中运行一个过程。这种滥用使得所有这些文档难以阅读,甚至没有用处。

因此,我的问题假定有多个应用程序,每个应用程序在单个进程中在单独的应用程序域中运行。

所有这些应用程序是否共享相同的GC和终结器线程?上面的文章中描述的问题(挂起终结器线程)是否会影响该进程中的所有应用程序?如果是 - 是否有一种解决方法(除了不使用不良应用程序),就像以某种方式发现终结器线程并将其发送给Thread.Abort?

以上都是因为我遇到了类似的问题。我的应用程序在单独的应用程序域中运行,作为第三方软件(Outlook)的插件。由于各种原因,我需要调用GC.Collect和GC.WaitForPendingFinalizers来完全释放COM引用(通常的互操作例程对Office / Outlook来说是不够的),当一个特定的其他第三方插件运行时,我的GC.WaitForPendingFinalizers永远挂起,所以我怀疑第三方添加的“坏”终结器。我无法控制替换/删除添加(客户端的要求),因此我必须自己弄清楚如何使它们共存。

1 个答案:

答案 0 :(得分:8)

看起来这个过程中每个CLR实例确实只是一个线程 - 目前,无论如何。这里有一些代码可以显示:

test.cs中:

using System;

class Test
{
    static void Main()
    {
        AppDomain.CreateDomain("First")
                 .ExecuteAssembly("ShowFinalizerThread.exe");
        AppDomain.CreateDomain("Second")
                 .ExecuteAssembly("ShowFinalizerThread.exe");
    }
}

ShowFinalizerThread.cs:

using System;
using System.Threading;

class ShowFinalizerThread
{
    static Random rng = new Random();

    ~ShowFinalizerThread()
    {
        Console.WriteLine("Thread/domain: {0}/{1}",
                          Thread.CurrentThread.ManagedThreadId,
                          AppDomain.CurrentDomain.FriendlyName);
        if (rng.Next(10) == 0)
        {
            Console.WriteLine("Hanging!");
            Thread.Sleep(2000);
        }
    }

    static void Main()
    {
        new Thread(LoopForever).Start();
    }

    static void LoopForever()
    {
        while (true)
        {
            new ShowFinalizerThread();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Thread.Sleep(300);
        };
    }
}

将每个编译为控制台应用程序,然后运行test.exe(从命令行最容易,IMO)。您会看到一个应用程序域的终结器会阻止另一个。

将来我不会惊讶地发现每个核心而不是每个AppDomain都有一个终结器线程 - 但听起来你仍然会遇到问题:(

你有最深切的同情(虽然不是解决方案) - 一旦我找到了Oracle Blob中的死锁。我们能够通过正确处理它来解决这个问题,但我知道并非所有事情都能很好地发挥作用 - 即使找到那个也真的很痛苦!