简体   繁体   English

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

[英]Why code in finally block doesn't execute?

It seems that finally block doesn't execute if it other than main thread execute code.如果除了主线程执行代码之外,finally 块似乎不会执行。 Is it possible to force execute finally in that case?在这种情况下是否可以最终强制执行?

Environment: VS 2010, .Net Framework 4.0.3环境: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");
        }
    }
}

Update:更新:

I found mentions and explanation about that case in MSDN:我在 MSDN 中找到了有关该案例的提及和解释:

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

When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException.当调用 Abort 方法来销毁线程时,公共语言运行时会引发 ThreadAbortException。 ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. ThreadAbortException 是一种可以被捕获的特殊异常,但它会在 catch 块结束时自动再次引发。 When this exception is raised, the runtime executes all the finally blocks before ending the thread .引发此异常运行时会在结束线程之前执行所有 finally 块 Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end .因为线程可以在 finally 块中进行无限计算或调用 Thread.ResetAbort 来取消中止,所以不能保证线程会永远结束 If you want to wait until the aborted thread has ended, you can call the Thread.Join method.如果要等到中止的线程结束,可以调用 Thread.Join 方法。 Join is a blocking call that does not return until the thread actually stops executing. Join 是一个阻塞调用,直到线程实际停止执行才会返回。

Note:笔记:

When the common language runtime (CLR) stops background threads after all foreground threads in a managed executable have ended, it does not use Thread.Abort.当公共语言运行时 (CLR) 在托管可执行文件中的所有前台线程都结束后停止后台线程时,它不会使用 Thread.Abort。 Therefore, you cannot use ThreadAbortException to detect when background threads are being terminated by the CLR.因此,您不能使用 ThreadAbortException 来检测 CLR 何时终止后台线程。


Foreground and Background Threads http://msdn.microsoft.com/en-us/library/h339syd0.aspx前台和后台线程http://msdn.microsoft.com/en-us/library/h339syd0.aspx

When the runtime stops a background thread because the process is shutting down, no exception is thrown in the thread.当运行时因为进程关闭而停止后台线程时,线程中不会抛出异常。 However, when threads are stopped because the AppDomain.Unload method unloads the application domain, a ThreadAbortException is thrown in both foreground and background threads.但是,当线程因为 AppDomain.Unload 方法卸载应用程序域而停止时,前台和后台线程都会抛出 ThreadAbortException。


So why at the end of application CLR doesn't use AppDomain.Unload method for unloading the application domain before end (kill) of main process?那么为什么在应用程序结束时 CLR 不使用 AppDomain.Unload 方法在主进程结束(杀死)之前卸载应用程序域? Because http://msdn.microsoft.com/en-us/library/system.appdomain.unload.aspx :因为http://msdn.microsoft.com/en-us/library/system.appdomain.unload.aspx

When a thread calls Unload, the target domain is marked for unloading.当线程调用 Unload 时,目标域被标记为卸载。 The dedicated thread attempts to unload the domain, and all threads in the domain are aborted.专用线程尝试卸载域,域中的所有线程都被中止。 If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload.如果一个线程没有中止,例如因为它正在执行非托管代码,或者因为它正在执行一个 finally 块,那么一段时间后,在最初调用 Unload 的线程中会抛出一个CannotUnloadAppDomainException。 If the thread that could not be aborted eventually ends, the target domain is not unloaded.如果无法中止的线程最终结束,则不会卸载目标域。 Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload , because it might not be possible to terminate executing threads.因此,在 .NET Framework 2.0 版中,域不保证卸载,因为它可能无法终止正在执行的线程。

Conclusion: In some cases I need to consider if will my code be executing in background or foreground thread?结论:在某些情况下,我需要考虑我的代码是在后台线程还是前台线程中执行? Is it possible that my code will not finished before application main thread ends all work?在应用程序主线程结束所有工作之前,我的代码有可能没有完成吗?

Your code is running in a background thread.您的代码正在后台线程中运行。 When you set the AutoResetEvent , your single foreground thread terminates (as you reach the end of the Main method) and the process is torn down "immediately".当您设置AutoResetEvent ,您的单个前台线程将终止(当您到达Main方法的末尾时)并且该进程“立即”被拆除。

In fact, I think it likely that your finally block starts executing, but as the first thing you do is sleep for two seconds, the process quits before it gets to your WriteLine call.事实上,我认为你的finally块很可能开始执行,但是你做的第一件事是休眠两秒钟,进程在它到达你的WriteLine调用之前退出。

If your Main method were still running, or any other foreground thread were keeping the process alive, you'd see your finally block complete as normal.如果您的Main方法仍在运行,或者任何其他前台线程使进程保持活动状态,您会看到您的finally块正常完成。 This isn't really a matter of "finally on other threads" - it's a matter of "the process only stays alive while there are foreground threads".这实际上不是“最终在其他线程上”的问题——而是“进程仅在有前台线程时才保持活动状态”的问题。

You can prevent the main method from exiting until the finally has executed.您可以防止 main 方法退出,直到 finally 执行。 There are many possible approaches.有许多可能的方法。

  • You can use synchronization to achieve this.您可以使用同步来实现这一点。 For example using a ResetEvent, similar to what you are already doing, or creating a thread explicitly and joining with it.例如使用 ResetEvent,类似于您已经在做的事情,或者显式地创建一个线程并加入它。

  • You could just a simple sleep or readline at the end of the Main method:您可以在Main方法的末尾添加一个简单的 sleep 或 readline:

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

Then the user can control when the program exits.然后用户可以控制程序何时退出。

  • You can use a non-background thread instead of a thread from the thread pool.您可以使用非后台线程而不是线程池中的线程。 Then the program won't exit until the thread has also terminated.然后程序不会退出,直到线程也终止。 This is probably the best and simpest option if you want your program not to terminate until the thread has completed.如果您希望程序在线程完成之前不终止,这可能是最好和最简单的选择。

I had this same issue for some time and after several different attempts, the following worked: refer to link https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2124?view=vs-2019我有一段时间遇到了同样的问题,经过几次不同的尝试,以下方法奏效:参考链接https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2124?view=vs-2019

Essentially it's this inside your Main().本质上它在你的 Main() 中。 I've been using it and it cleans things up for me as expected.我一直在使用它,它按预期为我清理了一切。

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

That way your finally clean up code runs and it still catches any exceptions.这样你的最终清理代码就会运行,它仍然会捕获任何异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM