Running and Controlling a Separate Thread






Running and Controlling a Separate Thread

Chapter 14 discussed delegates and events. Programming multiple threads with C# depends heavily on the syntax of delegates. In order to start a new thread, it is necessary to call a delegate that contains the code for the separate thread. Listing 15.1 provides a simple example, and Output 15.1 on page 554 shows the results.

Starting a Method in a Separate Thread

using System;
using System.Threading;                                          

public class RunningASeparateThread
{
  public const int Repetitions = 1000;

  public static void Main()
  {
      ThreadStart threadStart = new ThreadStart(DoWork);             
      Thread thread = new Thread(threadStart);                       
      thread.Start();                                                  
       for  (int count = 0; count < Repetitions; count++)
      {
          Console.Write('-');
      }
      thread.Join();
  }
  public static void DoWork()
  {
      for (int count = 0; count < Repetitions; count++)
      {
          Console.Write('.');
      }
   }
}

The code that is to run in a new thread appears in the DoWork() method. This method prints. to the console repeatedly during each iteration within a loop. Besides the fact that it contains code for starting another thread, the Main() method is virtually identical in structure to DoWork(), except that it displays -. The resulting output from the program is a series of dashes until the thread context switches, at which time the program displays periods until the next thread switch, and so on. (On Windows, it is possible to increase the chances of a thread context switch by using Start /low /b <program.exe> to execute the program. This will assign the entire process a lower priority, causing its threads to be interrupted more frequently, and thus causing more frequent thread switches.)

Starting a Thread

In order for DoWork() to run under the context of a different thread, you must first instantiate a System.Threading.ThreadStart delegate around the DoWork() method. Next, you pass the THReadStart delegate instance to the System.Threading.Thread constructor before commencing execution of the thread with a call to tHRead.Start().

Output 15.1.

................................-----------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--------------------------------------------------------------........... 
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
...................------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
----------------------------------------------........................... 
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
...----------------------------------------------------------------------
-------------------------------------------------------------------------
----------------------------------------------------------------------... 
.........................................................................
.........................................................................
.............................

In Listing 15.1, you instantiate the thread in two separate steps, explicitly instantiating a System.Threading.ThreadStart instance and assigning it to a variable before instantiating the System.Threading.Threadobject. As Listing 15.2 demonstrates, you could combine both statements, or you could use C# 2.0's delegate inference to avoid any explicit syntax to instantiate ThreadStart and instead pass DoWork directly into System .Threading.Thread's constructor.

Creating a Thread Using C# 2.0 Syntax

using System;
using System.Threading;

public class RunningASeparateThread
{

           // ...

     public static void Main()
     {
           // Requires C# 2.0                                                    
           Thread thread = new Thread(DoWork);                           
           thread.Start();                                               
           //... 
     }

     public static void DoWork()
     {
          // ...
     }
}

You can elect not to declare a ThreadStart variable in C# 1.0, but you cannot avoid explicit instantiation of the THReadStart instance.

Starting a thread simply involves a call to Thread.Start(). As soon as the DoWork() method begins execution, the call to THRead.Start() returns and executes the for loop in the Main() method. The threads are now independent and neither waits for the other. The output from Listing 15.1 and Listing 15.2 will intermingle the output of each thread, instead of creating a series of . followed by -.

Beginner Topic: Static and Instance THReadStart Methods

This example uses a static THReadStart-compatible method. As you learned in Chapter 14, it is also possible to use instance methods as delegates, explicitly identifying the object that contains the method (for example, this.Find, which is equivalent to new ThreadStart(Find)). You can also specify methods on other objects by prefixing the method name with the instance identifier (for example, song.Play).


Thread Management

Threads include a number of methods and properties for managing their execution.

Join()

Once threads are started, you can cause a "wait for completion" with a call to thread.Join(). The calling thread will wait until the tHRead instance terminates. The Join() method is overloaded to take either an int or a TimeSpan to support a maximum time to wait for thread completion before continuing execution.

IsBackGround

Another thread configuration option is the thread.IsBackGround property. By default, a thread is a foreground thread, meaning the process will not terminate until the thread completes. In contrast, setting the IsBackGround property to true will allow process execution to terminate prior to a thread's completion.

Priority

When using the Join() method, you can increase or decrease the thread's priority by setting the Priority to a new ThreadPriority enum value (Lowest, BelowNormal, Normal, AboveNormal, Highest).

ThreadState

A thread's state is accessible through the ThreadState property, a more precise reflection of the Boolean IsAlive property. The ThreadState enum flag values are Aborted, AbortRequested, Background, Running, Stopped, StopRequested, Suspended, SuspendRequested, Unstarted, and WaitSleepJoin. The flag names indicate activities that may occur on a thread. Two noteworthy methods are THRead.Sleep() and Abort().

Thread.Sleep()

THRead.Sleep() is a static method that pauses the current thread for a period. A single parameter (in milliseconds, or a TimeSpan) specifies how long the active thread waits before continuing execution. This enables switching to a different thread for a specific period.

This method is not for accurate timing. Returns can occur hundreds of milliseconds before or after the specified time.

Abort()

A thread's Abort() method causes a ThreadAbortException to be thrown within the target thread. The problem is that THRead.Abort() introduces uncertainty into the thread's behavior. In .NET 1.x, if the abort interrupts execution of a finally block, the remaining code within that block will never run. Furthermore, Abort() may fail because the aborted thread could have a catch block that handles the ThreadAbortException and calls Thread.ResetAbort(), or the thread could currently be running unmanaged code which will not throw the THReadAbortException until the code returns. Except in rare circumstances, developers should consider the Abort() method to be a last resort.

In .NET 2.0, if the abort interrupts execution of a finally block, then its effect will be delayed until the conclusion of the finally block (and any additional finally blocks within the call stack).



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows