为什么finally块中的代码不执行?

时间:2012-03-13 07:29:51

标签: c# .net multithreading try-catch-finally

如果除了主线程执行代码之外,似乎finally块不会执行。在这种情况下是否可以强制执行最终执行?

环境:VS 2010,.Net Framework 4.0.3

class Program
{
    static void Main(string[] args)
    {
        var h = new AutoResetEvent(false);

        ThreadPool.QueueUserWorkItem(
            obj => TestProc(h));

        h.WaitOne();
    }

    private static void TestProc(EventWaitHandle h)
    {
        try
        {
            Trace.WriteLine("Try");
            h.Set();
        }
        catch(Exception)
        {
            Trace.WriteLine("Catch");
        }
        finally
        {
            Thread.Sleep(2000);
            Trace.WriteLine("Finally");
        }
    }
}

更新

我在MSDN中找到了关于该案例的提及和解释:

ThreadAbortException类 http://msdn.microsoft.com/en-us/library/system.threading.threadabortexception.aspx

  

当调用Abort方法来销毁一个线程时,   公共语言运行库抛出ThreadAbortException。   ThreadAbortException是一个可以捕获的特殊异常,但它   将在catch块结束时再次自动引发。什么时候   引发此异常,运行时执行所有finally块   在结束帖子之前。因为线程可以做无界限   在finally块中计算或调用Thread.ResetAbort来取消   中止,无法保证线程将结束。如果   你想等到中止的线程结束,你可以调用   Thread.Join方法。加入是一个阻止调用,直到没有返回   线程实际上停止执行。

注意:

  

当公共语言运行库(CLR)之后停止后台线程时   托管可执行文件中的所有前台线程都已结束,但它没有   使用Thread.Abort。因此,您不能使用ThreadAbortException   检测CLR何时终止后台线程。


前景和背景主题 http://msdn.microsoft.com/en-us/library/h339syd0.aspx

  

当运行时停止后台线程因为进程正在关闭时,线程中不会抛出异常。但是,当因为AppDomain.Unload方法卸载应用程序域而停止线程时,前台和后台线程中都会抛出ThreadAbortException。


那么为什么在应用程序结束时CLR在主进程结束(kill)之前没有使用AppDomain.Unload方法来卸载应用程序域? 因为http://msdn.microsoft.com/en-us/library/system.appdomain.unload.aspx

  

当线程调用Unload时,目标域将被标记为卸载。   专用线程尝试卸载域,以及所有线程   域被中止。例如,如果线程没有中止   因为它正在执行非托管代码,或者因为它正在执行a   终于阻止,然后经过一段时间了   在最初的线程中抛出CannotUnloadAppDomainException   叫卸载。如果最终无法中止的线程   结束,目标域未卸载。因此,.NET Framework中的   版本2.0域不保证卸载,因为它可能不会   可以终止执行线程。

结论:在某些情况下,我需要考虑我的代码是否会在后台或前台线程中执行?在应用程序主线程结束所有工作之前,我的代码是否可能无法完成?

3 个答案:

答案 0 :(得分:17)

您的代码在后台线程中运行。当您设置AutoResetEvent时,您的单个前景线程会终止(当您到达Main方法的末尾时)并且该过程会立即“拆除”。

事实上,我认为你的finally阻止可能会开始执行,但是你要做的第一件事就是睡两秒钟,这个过程会在它到达你的{之前退出{1}}致电。

如果您的WriteLine方法仍在运行,或者任何其他前台线程使该进程保持活动状态,您会看到Main块正常完成。这实际上不是“最终在其他线程上”的问题 - 这是一个“只有前景线程才会保持活动状态”的问题。

答案 1 :(得分:4)

您可以阻止main方法退出,直到finally执行完毕。有很多可能的方法。

  • 您可以使用同步来实现此目的。例如,使用ResetEvent,类似于您正在进行的操作,或者显式创建线程并加入它。

  • 您可以在Main方法结尾处使用简单的睡眠或阅读线:

    h.WaitOne();
    Console.ReadLine();
    

然后用户可以控制程序何时退出。

  • 您可以使用非后台线程而不是线程池中的线程。然后程序将不会退出,直到线程也终止。如果您希望程序在线程完成之前不终止,这可能是最好和最简单的选项。

答案 2 :(得分:0)

一段时间以来,我遇到了相同的问题,经过多次尝试,以下方法起作用:请参考链接https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2124?view=vs-2019

本质上是在Main()中。我一直在使用它,它按预期为我清理了东西。

try { try {} finally {} } catch {}

这样,您最终清理的代码就会运行,并且仍然会捕获任何异常。