Labels

Monday, June 15, 2009

Threading || Interrupt & Abort

Scope of Work

 

·         Interrupt and Abort                               

1.      Interrupt                                              

2.      Abort                                                   


Interrupt and Abort

 

  • A blocked thread [WaitSleepJoin] can be released prematurely in one of two ways via another threads.
    • via Thread.Interrupt
    • via Thread.Abort

 

i.e A Thread blocked by ‘lock’ / ‘Sleep & Join’ can be released prematurely by another thread by calling its Interrput/Abort.

 

  • This must happen via the activities of another thread; the waiting thread is powerless to do anything in its blocked state.

 

Interrupt

 

  • Interrupting a thread only releases it from its current (or next) wait: it does not cause the thread to end.
  • Calling Interrupt on a blocked thread forcibly releases it, throwing a ThreadInterruptedException as below:

 

 

 

class Program

    {

        static void Main()

        {

            Thread t = new Thread(delegate()

            {

                try

                {

                    Thread.Sleep(Timeout.Infinite);

                }

                catch (ThreadInterruptedException)

                {

                    Console.Write("Forcibly ");

                }

                Console.WriteLine("Woken!");

            });

 

            t.Start();

            t.Interrupt();

        }

    }

 

Output –

 

Forcibly Woken!

 

 

 

 

 

  • If Interrupt is called on a thread that’s not blocked, the thread continues executing until it next blocks, at which point a ThreadInterruptedException is thrown.
  • Interrupting a thread arbitrarily is dangerous, however, because any framework or third-party methods in the calling stack could unexpectedly receive the interrupt rather than your intended code.

 

 

 

Abort

 

Abort -> AbortRequested -> Abort State

 

  • A blocked thread can also be forcibly ended/released via its Abort method.
  • This has an effect similar to Interrupt, except that a ThreadAbortException is thrown in place of ThreadInterruptedException.
  • The thread upon being aborted immediately enters the AbortRequested state. If it then terminates as expected, it goes into the Stopped state. The caller can wait for this to happen by calling Join.

 

  • Abort causes a ThreadAbortException, however even if we choose to handle the exception, the exception gets automatically re-thrown at the end of the catch block (Difference from Interrupt).
  • This automatic re-throw can be avoided by calling Thread.ResetAbort within the catch block. The thread then re- enters the Running state (from which it can potentially be aborted again).

 

 

 

class Terminator

    {

        static void Main()

        {

            Thread t = new Thread(Work);

            t.Start();

           

            Thread.Sleep(1000); t.Abort();

            Thread.Sleep(1000); t.Abort();

            Thread.Sleep(1000); t.Abort();

        }

 

        static void Work()

        {

            while (true)

            {

                try

                {

                    while (true);

                }

                catch (ThreadAbortException)

                {

                    Thread.ResetAbort();

                }

                Console.WriteLine("I will not die!");

            }

        }

    }

 

Output –

 

I will not die!

I will not die!

I will not die!

 

 

 

 

 

  • The another big difference, though, between Interrupt and Abort, is what happens when it's called on a thread that is not blocked. While Interrupt waits until the thread next blocks before doing anything, Abort throws an exception on the thread right where it's executing – may be not even in your code.

 

i.e. Abort will work on a thread in almost any state – running, blocked, suspended, or stopped.

However if a suspended thread is aborted, a ThreadStateException is thrown – this time on the calling thread – and the abortion doesn't kick off until the thread is subsequently resumed.

 

  • Aborting a non-blocked thread can have significant consequences, the details of which are explored in the later section.

 

 

Note –

ThreadAbortException is treated specially by the runtime, in that it doesn't cause the whole application to terminate if unhandled, unlike all other types of exception.

 

 

  • The thread upon being aborted immediately enters the AbortRequested state. If it then terminates as expected, it goes into the Stopped state.
  • The caller can wait for this to happen by calling Join.

 

 

class Terminator

    {

        static void Main()

        {

            Thread t = new Thread(Work);

            Console.WriteLine(t.ThreadState); // Unstarted

 

            t.Start();

            Thread.Sleep(1000);

            Console.WriteLine(t.ThreadState); // Running

            t.Abort();

            Console.WriteLine(t.ThreadState); // AbortRequested

            t.Join();

            Console.WriteLine(t.ThreadState); // Stopped

        }

 

        static void Work()

        {

            while (true)

            {

                try

                {

                    while (true);

                }

                catch (ThreadAbortException)

                {}               

            }

        }

    }

 

 

 

 

 

Complications with Abort

 

Lingering:

 

o        Assuming an aborted thread doesn't call ResetAbort, then you might expect it to terminate fairly quickly.

o        But it does not happens this way - The thread may remain on death row for quite some time.

o        Here are a few factors that may keep it lingering in the AbortRequested state:

 

    • Static class constructors are never aborted part-way through.
    • All catch/finally blocks are honored, and never aborted mid-stream.
    • If the thread is executing unmanaged code when aborted, execution continues until the next managed code statement is reached.

 

To avoid this lingering to be undeterminsitic, the caller can wait for this to happen by calling Join.

But sometimes .NET framework itself often calls unmanaged code, possiblily remaining there for long periods of time. In these cases, call Join with a timeout  feature.

 

 

Abort Safe:

 

            As we know that any thread can fire Abort on any other thread. This unusual abortion [Unsafe abort] can lead to lots of problems.

            For e.g

 

           

 

StreamWriter w;

w = File.CreateText ("myfile.txt");

try

{

   w.Write ("Abort-Safe");

}

finally

{

   w.Dispose();

}

 

 

 

 

It's possible for an Abort to fire after the StreamWriter is created, but before the try block begins. In fact, by digging into the IL, one can see that it's also possible for it to fire in between the StreamWriter being created and assigned to w. Either way, the Dispose method in the finally block will be circumvented, resulting in an abandoned open file handle – preventing any subsequent attempts to create myfile.txt until the application domain ends. This will be treated as ‘Unsafe abort’.

 

Then how one should go about writing an abort-friendly method. The most common workaround is not to abort another thread at all.

Otherwise below are the three Abort Safe situations.

 

·         Calling Abort on one's own thread is one circumstance in which Abort is totally safe.

·         Another is when you can be certain the thread you're aborting is in a particular section of code, usually by virtue of a  synchronization mechanism such as a Wait Handle or Monitor.Wait.

·         A third instance in which calling Abort is safe is when you subsequently tear down the thread's application domain or process. i.e After calling Abort, one simply tears down the application domain, thereby releasing any resources that were improperly disposed.

 

 

Hope this is clear now.

 

Thanks & Regards,

Arun Manglick || Senior Tech Lead

 

No comments:

Post a Comment