Thread.Join似乎错误地返回false

时间:2012-11-12 15:51:24

标签: c# multithreading

我正在使用Thread.Join(int millisecondsTimeout)来终止一些AppDomain s。

我经常收到一条错误消息,指出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

值得添加每个AppDomain的内容。每个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);
    }
}

实际上我知道“Main”线程将抛出ThreadAbortException,跳转到finally语句并确保Timer队列在退出之前完全耗尽。

所有Timer s虽然都在tick方法中记录。所以我可以确定计时器队列上没有任何内容,_timer.Dispose(_timerWaitHandle)会立即返回。

无论是否这样做,我AppDomain三个Unload中的至少一个将在5秒内完成。{{1}}

2 个答案:

答案 0 :(得分:3)

MSDN Library中有关Unload()的文章中详细记录了这一点:

  

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

     

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

因此,您需要找出为什么您的程序在该appdomain中运行了一个线程以及为什么它拒绝中止。例如,当它被隐藏在非托管代码中时。使用Debug + Windows + Threads查看它们。

答案 1 :(得分: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工具)应该显示它