What Else Can Be Done to a Thread?

You can interrupt a thread, suspend a thread, resume a thread, and abort a thread. Figure 11.3 shows an application running whereby all these operations are interacting. The full source to this application is available in the ThreadAbort directory.

Figure 11.3. Thread manipulation.


Only one button is enabled at startup: the Start button. By pressing this button, a Thread is started. When the Thread is started, it waits for the ManualResetEvent to be signaled. One of the first things that you might try is to start it again. Notice that the Start button is not disabled after a Thread has started. If the Thread is Started after it has already been Started, then an exception will be thrown similar to that shown in Figure 11.4.

Figure 11.4. Restarting a running thread.


The Suspend and Resume methods can also suspend and resume a Thread. When a Thread is first created, it is in a state of Unstarted. Starting a Thread puts the Thread in a Running state. When a Thread suspends itself because of a wait (WaitOne, WaitAll, and so on), Sleep, or Join, the Thread will be in a WaitSleepJoin state. If the Thread is in a wait state (which most likely is the case here), it is actually in unmanaged code (probably CoWaitForMultipleObjects, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx, WaitForMultpleObjects, or WaitForMultipleObjectsEx). The exact unmanaged code that the Thread is in depends on the OS and the apartment model under which the Thread is executing. When a Suspend is issued to a Thread that is in a WaitSleepJoin state, the only guarantees that the CLR makes is that no unmanaged code will execute when a Thread is Suspended. Until the code exits the unmanaged code, the Thread is waiting to be suspended and its state is SuspendRequested. (It is also in a WaitSleepJoin state. Remember that the ThreadState values are flags, so a Thread can theoretically have any combination of the flags.) To reverse the Suspend, you can can call Resume, which will “unfreeze” the Thread. Calling Suspend any number of times has the same effect as calling it once. Calling Resume on a Suspended Thread puts the Thread in a Running state. If Resume is called on a Thread that is in any other state than Suspend or SuspendRequested, a ThreadStateException will be thrown.

A Thread can be interrupted with the Interrupt method call. This method causes the Thread to throw a ThreadInterruptedException. It is up to the application to decide what to do after an interrupt occurs. For this simple demonstration, the Thread was simply put back into the processing loop. Listing 11.7 shows a small code snippet from the application that shows one way that an interrupt could be handled. Basically, a message is printed and the Thread goes back to waiting.

Listing 11.7. Thread Interrupt
catch(ThreadInterruptedException e)
{
    string msg = "ThreadInterruptedException occured
" + e.ToString();
    BeginInvoke(messageDelegate, new object[] {msg, MessageType.Error} );
    // Yeah, I know about "GOTO considered harmful, Edsger W. Dijkstra" at
    // http://www.net.org/html/history/detail/1968-goto.html, but I saw no
    // other choice here.
    goto start;
}

The last operation that can be performed on a Thread is to stop it with Abort. This might take some re-education for those who grew up with the managed threads. The API TerminateThread has been available for some time, but its use was discouraged for all but the most extreme circumstances. Its use was discouraged because it caused a thread to terminate with no chance to do any cleanup. Because of this, a programmer typically would have a special event that was signaled when the thread was supposed to exit. With managed threads, Abort is the preferred method for terminating a Thread.

Like Interrupt, Abort causes an exception to be thrown. This exception is a special exception called the ThreadAbortException. Listing 11.8 shows an example of how to handle this exception.

Listing 11.8. Thread Abort
catch(ThreadAbortException e)
{
    string msg = "ThreadAbortException occured
" + e.ToString();
    BeginInvoke(messageDelegate, new object[] {msg, MessageType.Error} );
    // Notice that this is ignored.
    goto start;
}

Abort is a special exception because although it can be caught, any code that causes the exception to be ignored is silently ignored. Notice in Listing 11.8 that it was attempted to handle Abort just like the Interrupt. The goto start statement is silently ignored, and processing proceeds to the finally block. If Abort is called on a Suspended Thread, it puts the Thread (and the application) in deadlock. (You can try this by commenting out the code that disables the Abort button when the Suspend button is pressed.) If you have the correct permission set, you can essentially ignore the Abort by issuing a Thread.ResetAbort call just before the goto statement. After calling Thread.ResetAbort, you can handle the exception just like ThreadInterruptException. Be aware that using this function can defeat AppDomain.Unload, the host's attempt to defeat denial-of-service attacks, prevent HttpResponse.End from working correctly, as well as other things. Using Thread.ResetAbort is not a nice thing to do, and in general, it should be disallowed. To use this function, the code has to have ControlThread permission, so it probably would be a good idea to deny this permission by default.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.154.208