Interrupting a Waiting Thread
You can rudely awaken a sleeping or waiting thread by calling the
Interrupt() method of the thread class:
public void Interrupt();
Background threads are a poor man's solution for application shutdown.
|
|
Calling
Interrupt() unblocks a sleeping thread (or a waiting thread, such as a thread that called
Join() on another thread), and throws an exception of type
ThreadInterruptedException in the unblocked thread. If the code the thread executes does not catch that exception, then the thread is terminated by the runtime. If the thread is not sleeping (or waiting), and a call to
Thread.Interrupt() is made, then the next time the thread tries to go to sleep (or wait), then .NET will immediately throw in its call stack the exception of type ThreadInterruptedException. Again, you should avoid relying on drastic solutions such as throwing exceptions to unblock another thread. Use .NET synchronization objects instead, to gain the benefits of structured and deterministic code flow. In addition, calling
Interrupt() does not interrupt a thread that is executing unmanaged code via interop. Note that calling
Interrupt() does not interrupt a thread that is in the middle of a call to
SpinWait(), because that thread is actually not waiting at all (as far as the operation system is concerned).
Aborting a Thread
The Thread class provides an
Abort() method, intended to forcefully terminate a .NET thread. Calling
Abort() throws an exception of type ThreadAbortExceptionin the thread being aborted.
ThreadAbortException is a special kind of exception: even if the thread method uses exception handling to catch exceptions, such as:
public void MyThreadMethod()
{
try
{
while(<some condition>)
{
<Do some work>
}
}
catch
{
//Handle exceptions here
}
}
After the
catch statement is executed, .NET re-throws the
ThreadAbortException to terminate the thread. This is done so that non-structured attempts to ignore the abort by jumping to the beginning of the thread method will simply not work:
//Code that does not work when
//ThreadAbortException is thrown.
public void MyThreadMethod()
{
Resurrection:
try
{
while(<some condition>)
{
<Do some work>
}
}
catch
{
goto Resurrection;
}
}
If
Abort() is called before the thread is started, .NET will never start the thread once
Thread.Start() is called. If
Thread.Abort() is called while the thread is blocked (either by calling
Sleep(), or
Join(), or if the thread is waiting on one of the .NET synchronization objects), .NET unblocks the thread and throws
ThreadAbortException in it. However, you cannot call
Abort() on a suspended thread. Doing so will result on the calling side with an exception of type ThreadStateException, with the error message "Thread is suspended; attempting to abort." In addition, .NET will terminate the suspended thread without letting it handle the exception.
The Thread class has an interesting counter-abort methodthe static
ResetAbort() method:
public static void ResetAbort();
Avoid controlling the application flow by setting Thread priorities.
|
|
Calling
Thread.ResetAbort() in a
catch statement prevents .NET from re-throwing
ThreadAbortException at the end of the
catch statement:
catch(ThreadAbortException exception)
{
Trace.WriteLine("Refusing to die");
Thread.ResetAbort();
//Do more processing or even go to somewhere
}
Note that
ResetAbort() demands the
ControlThread security permission.
Terminating a thread by calling
Abort() is not recommended for a number of reasons. The first is that it forces the thread to perform an ungraceful exit. Often the thread will need to release resources it holds and perform some sort of a cleanup before terminating. You can of course handle exceptions, and put the cleanup code in the
finally method, but you typically want to handle the unexpected errors that way, and not use it as the standard way of terminating your thread. Never use exception to control the normal flow of your applicationit is akin to non-structured programming using
goto.
Second, nothing prevents the thread from abusing .NET and either performing as many operations as it likes in the
catch statement or jumping to a label or calling
ResetAbort(). If you want to terminate a thread, you should do so in a structured manner using the .NET synchronization objects. You should signal the Thread method to exit using a member variable or event.
Calling
Thread.Abort() has another liability: if the thread makes an interop call (using COM Interop or P-Invoke), the interop call may take a while to complete. If
Thread.Abort() is called during the interop call, .NET will not abort the thread, but lets it complete the interop call, only to abort it when it returns. This is yet another reason why
Thread.Abort() is not guaranteed to succeed, or succeed immediately.