最终可以跳过ThreadAbortException

本文关键字:ThreadAbortException | 更新日期: 2023-09-27 18:25:48

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

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在单独线程中的函数吗?

最终可以跳过ThreadAbortException

文档说ThreadAbortException是一个可以捕获的特殊异常,但它将在捕获块结束时自动再次引发。当引发此异常时,运行时会在结束线程之前执行所有finally块。因为线程可以在finally块中进行无边界计算,或者调用thread.ResetArtrt来取消中止,所以无法保证线程会结束。

我很确定你的线程被转储了,因为你退出了这个方法并丢失了对它的引用,所以它被垃圾回收器收集了。试着让testThread变量成为类的字段成员,看看会发生什么。

或者,由于线程是并行运行的,您有一个竞争条件:在启动的测试线程可以输出finally数据之前,主线程已经结束(异常非常昂贵,并且需要时间才能到达catch或finally块)。

工作线程函数中的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块之前执行该处理程序。这只是中止线程时应该注意的令人沮丧和意外的执行顺序的另一个例子。

通常不应跳过finally

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

如果你把Console.ReadLine()放在程序的末尾,会发生什么?