5

Everything I've read claims an abort on a thread will execute the finally block before ending from a ThreadAbortException. I wanted to confirm this so I can plan on how to handle some 3rd party code that can hang indefinitely. However the following test has me confused:

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());
}

What I find when running this is that console never mentions entering the finally block. The application continues on after the .abort call as if there is no finally block at all. Am I doing something wrong? Shouldn't control pass to the finally block before reaching the final write to console or is the execution order still a function of the fact that the finally is in a separate thread or something?

user1807768
  • 687
  • 1
  • 7
  • 11
  • I ran the code and I get `test thread entered FINALLY at 3:21 PM`. – Dustin Kingen Aug 01 '13 at 19:22
  • Not tried, but I suspect the code will behave differently between Debug and release/no debugger attached configurations as infinite loop likely to be JITed into something that can't be interrupted in release build. – Alexei Levenkov Aug 01 '13 at 19:29
  • 2
    Calling `Thread.Abort` is like stopping a car by shooting the driver in the head. The car will stop, but there's no telling what might happen in the meantime. You really shouldn't *ever* need to call `Thread.Abort`. If you do, then you need to revisit your application's design. – Jim Mischel Aug 01 '13 at 19:29
  • Pretty much everyone spotted the problem in the time it took me to get lunch. Finally isn't skipped, it just is in another thread so execution doesn't happen necessarily right after the .abort call is made. I incorrectly assumed .abort would be a serial execution that goes right to the finally block then back to the main thread. The test thread sometimes doesn't actually end for a few seconds after the main thread resumed. – user1807768 Aug 01 '13 at 19:55

3 Answers3

8

Docs say: 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.

I'm pretty sure your thread is being dumped because you exit the method and lose a reference to it, and so it gets collected by the Garbage Collector. Try making the testThread variable a field member of the class and see what happens.

That, or you have a race condition since the threads run in parallel: the main thread is finishing before the spun-up test thread can output the finally data (Exceptions are expensive and take time to reach catch or finally blocks).

Haney
  • 32,775
  • 8
  • 59
  • 68
  • 3
    Running threads don't generally get collected. See: http://stackoverflow.com/questions/81730/what-prevents-a-thread-in-c-sharp-from-being-collected –  Aug 01 '13 at 19:28
  • My experience has differed, however it seems the output is perhaps a race condition between the main thread and spawned thread? Updated my answer with that, thanks for the input @ebyrob – Haney Aug 01 '13 at 19:30
  • I tried it again a few times after Romoku mentioned it worked for him. This confirms my suspicion that the thread doesn't necessarily get aborted before control returns to the main thread. I added a .join after the abort and now it stays in the finally without ever calling the final console write in the main thread. So DavidH is right, their is some race condition ish type of thing going on here. – user1807768 Aug 01 '13 at 19:47
5

The finally block in the worker thread function is executed on the worker thread which is parallel to the main thread. It's a race condition. You can not tell which of worker thread finally or main thread code after abort call get executed sooner. If you need a synchronous abort then you have to put something like that:

        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());

Keep in mind that if you have legacy exception handling enabled (see http://msdn.microsoft.com/en-us/library/ms228965.aspx) and you specified the AppDomain_UnahandledException event handler then the ThreadAbortException will lead execution to that handler BEFORE the finally block in the worker thread function. This is just another example of frustrating and unexpected execution order one should be aware of while aborting threads.

Zverev Evgeniy
  • 3,643
  • 25
  • 42
2

The finally shouldn't normally be skipped.

It's possible that the console app (assuming it is one) is exiting before the finally block runs (since there is no wait after you call Thread.Abort()).

What happens if you put a Console.ReadLine() right at the end of the program?

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276