3. Aborting a Thread
The Thread class provides an Abort( ) method, which can forcefully try to terminate a .NET thread. Calling Abort( ) throws an exception of type ThreadAbortException in the thread being aborted. ThreadAbortException
is a special kind of exception. Even if the thread method uses
exception handling to catch exceptions, as in the following example
code:
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
that ignore the abort by jumping to the beginning of the thread method
simply don't work:
//Code that doesn't work when ThreadAbortException is thrown
public void MyThreadMethod( )
{
Resurrection:
try
{
while(<some condition>)
{
<Do some work>
}
}
catch
{
goto Resurrection;
}
}
Using non-structured goto instructions is strongly discouraged in any case. Never use goto, except to fall through in a C# switch statement. |
|
The Abort( ) method has two overloaded versions:
public sealed class Thread
{
public void Abort( );
public void Abort(object stateInfo)
//Other methods and properties
}
One version allows the party that calls Abort( ) to provide a generic parameter of type object called stateInfo. stateInfo
can convey application-specific information to the aborted thread, such
as why it's being aborted. The aborted thread can access the stateInfo object via the ExceptionState public property of the ThreadAbortException class, if the thread is using exception handling.
Example 2 demonstrates using Abort( )
to terminate a thread. The example creates a new thread, whose thread
method simply traces an incrementing integer to the Output window. The
thread method uses exception handling, and it traces to the Output
window the information passed to it using the stateInfo parameter of Abort( ). Note that the thread that called Abort( ) uses Join( )
to wait for the thread to die. This is the recommended practice,
because the thread can perform an open-ended number of operations in
its catch and finally exception-handling statements.
Example 2. Terminating a thread using Abort( )
public class MyClass { public void DoWork( ) { try { int i = 0; while(true) { Trace.WriteLine(i++); }
} catch(ThreadAbortException exception) { string cause; cause = (string)exception.ExceptionState; Trace.WriteLine(cause); } } } MyClass obj = new MyClass( ); Thread workerThread = new Thread(obj.DoWork); workerThread.Start( );
/* Do some work, then: */
workerThread.Abort("Time to go"); workerThread.Join( );
|
If Abort( ) is called before the thread is started, .NET doesn't start the thread when 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 a ThreadAbortException in it. However, you can't call Abort( ) on a suspended thread. Doing so results in an exception of type ThreadStateException
on the calling side, with the error message "Thread is suspended;
attempting to abort." .NET then terminates the suspended thread without
letting it handle the exception.
The Thread class also has an interesting counter-abort method—the static ResetAbort( ) method:
public static void ResetAbort( );
Calling Thread.ResetAbort( ) in a catch statement prevents .NET from re-throwing a ThreadAbortException at the end of the catch statement:
catch(ThreadAbortException exception)
{
Trace.WriteLine("Refusing to die");
Thread.ResetAbort( );
//Do more processing or even goto somewhere
}
ResetAbort( ) requires the ControlThread security permission. |
|
Terminating a thread by calling Abort( )
isn't recommended, for a number of reasons. The first is that it forces
the thread to perform an ungraceful exit. Often, the thread needs to
release resources it holds and perform some sort of cleanup before
terminating. You can, of course, handle exceptions and put the cleanup
code in the finally method, but you typically want to handle
unexpected errors that way and not use it as the standard way to
terminate a thread. Second, nothing prevents the thread from abusing
.NET and either performing as many operations as it likes in the catch statement, 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 by 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
doesn't abort the thread; it lets the thread complete the interop call,
only to abort it when it returns. This is another reason why Thread.Abort( ) isn't guaranteed to succeed (or succeed immediately). |