4

Thread.Join(int millisecondsTimeout)用来终止一些AppDomains.

我经常收到一条错误消息,指出 AppDomain 未在 5 秒内终止。在单步调试器中,我看到AppDomain.Unload()调用在 5 秒内轻松终止,但Thread.Join返回 false。

我哪里错了?

var thread = new Thread(
    () =>
    {
        try
        {
            AppDomain.Unload(someAppDomain);
        }
        catch (ArgumentNullException)
        {

        }
        catch (CannotUnloadAppDomainException exception)
        {
            // Some error message
        }
    });

thread.Start();
const int numSecondsWait = 5;

if (!thread.Join(1000 * numSecondsWait))
{
    // Some error message about it not exiting in 5 seconds
}

编辑 1

值得添加每个AppDomains 所做的事情。每个AppDomain人至少有一个Timer。代码大致如下所示,(请记住,为了便于阅读,我已将大量类合并为一个类)。

static void Main(string[] args)
{
    _exceptionThrown = new EventWaitHandle(false, EventResetMode.AutoReset);
    _timer = new Timer(TickAction, null, 0, interval);
    try
    {
        _exceptionThrown.WaitOne();
    }
    finally
    {
        _timer.Dispose(_timerWaitHandle);
        WaitHandle.WaitAll(_timerWaitHandle);
    }
}

实际上,我知道“主”线程会抛出一个ThreadAbortException, 跳转到 finally 语句并确保Timer队列在退出之前完全排空。

Timer当它们在 tick 方法中时,所有的s 都会记录下来。所以我几乎可以肯定计时器队列中没有任何内容,并且_timer.Dispose(_timerWaitHandle)立即返回。

不管做与不做,至少三个我ing中的一个不会在5秒内完成。AppDomainUnload

4

2 回答 2

3

其原因在 Unload() 的 MSDN 库文章中有详细记录:

在 .NET Framework 2.0 版中,有一个专门用于卸载应用程序域的线程。这提高了可靠性,尤其是在托管 .NET Framework 时。当线程调用 Unload 时,目标域被标记为卸载。专用线程尝试卸载域,并且域中的所有线程都被中止。如果一个线程没有中止,例如因为它正在执行非托管代码,或者因为它正在执行 finally 块,那么在一段时间后,在最初调用 Unload 的线程中会抛出一个 CannotUnloadAppDomainException。如果无法中止的线程最终结束,则不会卸载目标域。因此,在 .NET Framework 2.0 版中,域不能保证卸载,因为它可能无法终止正在执行的线程。

域中的线程使用 Abort 方法终止,该方法在线程中引发 ThreadAbortException。尽管线程应该立即终止,但它可以在 finally 子句中继续执行不可预测的时间。

所以你需要找出为什么你的程序有一个线程在该应用程序域内运行,以及为什么它拒绝中止。例如,当它隐藏在非托管代码中时很常见。使用 Debug + Windows + Threads 来查看它们。

于 2012-11-12T16:17:38.547 回答
3

如果您想确保 appdomains 始终在 5 秒内卸载,您可以尝试测量它。例如使用这样的东西:

var stopwatch = System.Diagnostics.Stopwatch.StartNew();
AppDomain.Unload(someAppDomain);
long elapsedMillis = stopwatch.ElapsedMilliseconds;
System.Diagnostics.Trace.Writeline("Unload duration: " + elapsedMillis + " ms");

Visual Studio 的输出窗口(或 sysinternals 的 DebugView 工具)应该显示它

于 2012-11-12T16:18:50.913 回答