5

我读过的所有内容都声称线程中止将在从 ThreadAbortException 结束之前执行 finally 块。我想确认这一点,以便我可以计划如何处理一些可以无限期挂起的第 3 方代码。然而,以下测试让我感到困惑:

public void runTest(DateTime deadline)
{
    testThread = new Thread(() => 
    {
        try 
        {
            Console.WriteLine("test thread started at " + DateTime.Now.ToShortTimeString());
            while (true) { }
        }
        finally
        {
            Console.WriteLine("test thread entered FINALLY at " + DateTime.Now.ToShortTimeString());
            while (true) { }
        }
    });
    testThread.Start();
    while (testThread.IsAlive && deadline.Subtract(DateTime.Now).TotalSeconds > 0)
    {
        Console.WriteLine("main thread while loop " + DateTime.Now.ToShortTimeString());
        Thread.Sleep(10000);
    }
    if (testThread.IsAlive)
        testThread.Abort();
    Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString());
}

我在运行时发现控制台从未提及进入 finally 块。应用程序在 .abort 调用之后继续运行,就好像根本没有 finally 块一样。难道我做错了什么?在到达对控制台的最终写入之前,控制是否不应该传递给 finally 块,或者执行顺序仍然是 finally 在单独的线程中或其他什么事实的函数?

4

3 回答 3

7

文档说:ThreadAbortException是一个可以被捕获的特殊异常,但它会在 catch 块的末尾自动再次引发。当引发此异常时,运行时会在结束线程之前执行所有 finally 块。因为线程可以在 finally 块中进行无限计算或调用 Thread.ResetAbort 来取消中止,所以不能保证线程将永远结束。

我很确定您的线程正在被转储,因为您退出了该方法并丢失了对它的引用,因此它被垃圾收集器收集。尝试使 testThread 变量成为类的字段成员,看看会发生什么。

那,或者你有一个竞争条件,因为线程并行运行:主线程在旋转的测试线程可以输出 finally 数据之前完成(异常是昂贵的并且需要时间才能到达 catch 或 finally 块)。

于 2013-08-01T19:22:23.517 回答
5

工作线程函数中的 finally 块在与主线程并行的工作线程上执行。这是一个竞赛条件。在中止调用之后,您无法判断哪个工作线程最终或主线程代码执行得更快。如果您需要同步中止,那么您必须输入以下内容:

        if (testThread.IsAlive)
        {
            testThread.Abort();

            bool blnFinishedAfterAbort = testThread.Join(TimeSpan.FromMilliseconds(1000));
            if (!blnFinishedAfterAbort)
            {
                Console.WriteLine("Thread abort failed.");
            }
        }
        Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString());

请记住,如果您启用了旧式异常处理(请参阅http://msdn.microsoft.com/en-us/library/ms228965.aspx)并且您指定了AppDomain_UnahandledException事件处理程序,那么 ThreadAbortException 将导致执行到该处理程序之前工作线程函数中的 finally 块。这只是在中止线程时应该注意的令人沮丧和意外的执行顺序的另一个示例。

于 2013-08-01T19:28:16.297 回答
2

通常finally不应跳过。

控制台应用程序(假设它是一个)可能在finally块运行之前退出(因为在您调用之后没有等待Thread.Abort())。

如果你Console.ReadLine()在程序的末尾加上一个 right 会发生什么?

于 2013-08-01T19:26:48.733 回答