8

It seems that finally block doesn't execute if it other than main thread execute code. Is it possible to force execute finally in that case?

Environment: 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:

ThreadAbortException Class 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. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. 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. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.

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. Therefore, you cannot use ThreadAbortException to detect when background threads are being terminated by the CLR.


Foreground and Background Threads 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.


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? Because http://msdn.microsoft.com/en-us/library/system.appdomain.unload.aspx:

When a thread calls Unload, the target domain is marked for unloading. 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. 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.

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?

AndreyR
  • 294
  • 4
  • 15

3 Answers3

17

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".

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.

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. 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".

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think I understood that than I was writing that code, but in my opinion this is normal operation and finally block guarantied for executing in this circumstances. This is normal flow of execution, but finally was aborted. This is surprising behavior for me. – AndreyR Mar 13 '12 at 07:36
  • 1
    @Andir: Why is it surprising? You *asked* to run the code on a background thread, so it *did*. If you don't want that, don't do that! Use a foreground thread instead. To be frank I think it would be *much* more surprising if you ran code on a background thread and it *prevented* your application from exiting. That's not what background is supposed to mean. – Mark Byers Mar 13 '12 at 07:38
  • A thread can't run any code, finally block or not, if you terminate it. If you want to wait for it to finish before terminating the thread, you have to code that. – David Schwartz Mar 13 '12 at 07:38
  • Sleep was added only for example, in real code finally block doesn't execute too. – AndreyR Mar 13 '12 at 07:38
  • @Andir: Well as I say, I suspect it *starts* executing... but just doesn't get very far. (I tried it in a console app with a `Console.WriteLine` at the start of the finally block, before the sleep, and it showed that one. If this is all surprising behaviour for you, it's presumably just because you weren't aware of the process termination policy. What would you expect a background thread in an infinite loop to do? Keep the process alive forever? If that were the case, what would the difference between a foreground and background thread be? – Jon Skeet Mar 13 '12 at 07:42
  • So, as I understand I cannot guarantee that resources which I am using in background thread will be correctly released if main thread was ended (expected or unexpected). For example, if I have background thread which sometimes (by timer) clears some file or database caches, I cannot guarantee correctness of it operations. It's very sad. – AndreyR Mar 13 '12 at 08:38
  • 2
    @Andir: You will *always* have to cope with the possibility of a process going down suddenly anyway. Note that there are alternative shutdown hooks you can add if you need to. – Jon Skeet Mar 13 '12 at 09:12
4

You can prevent the main method from exiting until the finally has executed. 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.

  • You could just a simple sleep or readline at the end of the Main method:

    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.
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
0

I had this same issue for some time and after several different attempts, the following worked: refer to link https://learn.microsoft.com/en-us/visualstudio/code-quality/ca2124?view=vs-2019

Essentially it's this inside your 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.

Goku
  • 197
  • 1
  • 7