Threading


Threading is a programming technique that permits two or more programming execution points (a place in your code) to run in an application at the same time. Threading is great because you can develop a single application that can do multiple things at the same time. For example, in Chapter 2, we described how to perform I/O over a network using synchronous programming techniques where your application could service only reading or writing data on a network and nothing else. But what if you want your application to handle several connections at the same time? Threading can help by allowing you to have multiple execution points in your code to service multiple connections in parallel. One thing to note is that you should be cautious about using too many threads to service connections, which we ll discuss later in the chapter. Figure 3-1 demonstrates threading pictorially by describing a generic application having three threads that write data to a file, read data from a network, and update a screen.

click to expand
Figure 3-1: An application running three threads

The .NET Framework provides a Thread class in the System.Threading namespace that allows you to create and manage threads in your application.

Creating a Thread

Creating a thread is simple. All you have to do is create a thread object using the System.Threading.Thread class and pass a delegate method to the Thread constructor that will be called when the thread starts running. A delegate method can be any method that does not take a parameter and does not return a value. The following code fragment demonstrates one possible way to develop a delegate method:

C#

 voidMyThreadMethod() { //Dosomethingusefulhere } 

Visual Basic .NET

 SharedSubMyThreadMethod() Dosomethingusefulhere EndSub 

Once a delegate method is defined, you can create a thread. Creating a thread requires that you identify a delegate method to the Thread class constructor. A special class named ThreadStart allows you to define a delegate method to a thread. Once the delegate method is defined, you simply pass a ThreadStart object to the Thread constructor. The following code fragment shows how to define a ThreadStart delegate method using the MyThreadMethod method mentioned above and create a thread:

C#

 ThreadMyThread=null; try { ThreadStartThreadMethod=newThreadStart(MyThreadMethod); MyThread=newThread(ThreadMethod); } catch(Exceptione) { Console.WriteLine(Failedtocreatethreadwitherror:   +e.Message); } 

Visual Basic .NET

 DimMyThreadAsThread=Nothing Try DimThreadMethodAsThreadStart=_ NewThreadStart(AddressOfMyThreadMethod) MyThread=NewThread(ThreadMethod) CatcheAsException Console.WriteLine(Failedtocreatethreadwitherror:  _ +e.Message) EndTry 

Once you ve successfully created a thread, you can begin controlling how the thread operates within your program. When the thread is created, nothing actually happens from your application s point of view; therefore, you re required to call the thread s Start method to get the thread running. Calling Start actually tells the operating system to begin scheduling your thread for processing. The following code fragment demonstrates how to start a thread using the MyThread object created earlier:

C#

 try { MyThread.Start(); } catch(Exceptione) { Console.WriteLine(Thethreadfailedtostartwitherror:   +e.Message); } 

Visual Basic .NET

 Try MyThread.Start() CatcheAsException Console.WriteLine(Thethreadfailedtostartwitherror:  _ +e.Message) EndTry 

Once the thread is started, your delegate method will begin to run. A thread can be started only once. If you try to start the thread twice, a ThreadStateException will be raised, indicating that the thread can t be started twice. Also, when the thread delegate method completes, you can t restart the thread; if you try to, an exception will be raised.

Your application is allowed to run only a finite number of threads at the same time. If you try to start a thread while too many threads are running, Start will throw an OutOfMemoryException . You might be wondering how many threads can start running in your application. It depends on operating system resources such as memory. In general, it s a bad practice to run too many threads at the same time because scheduling threads for execution takes up operating system resources such as the computer processor and memory. It s important to realize that a computer processor can actually service only one thread at a time, and when two threads are running, the operating system is switching control from one to the other. Operating system thread scheduling gives the application the illusion that each thread is running at the same time. If the application is running on a multiprocessor machine, the operating system can truly execute multiple threads at the same time. It s important to understand that threading does not increase the amount of computing you can do in your application, but instead, it allows you to create more dynamic applications that can interact better with multiple resources at the same time instead of just doing one thing at a time.

Note  

The .NET Framework features code access security for many of the managed classes in the .NET Framework. However, threads can t be controlled by code access security features in the .NET Framework version 1. A control flag for threads can be accessed by using the System.Security.Permissions.SecurityPermissionFlag.ControlThread permission from the System.Security.Permissions.SecurityPermission class. However, although the control flag exists, the security permission flag does not have any effect on controlling threads from a security zone.

Controlling a Thread

A thread has several operating states that identify its current operating status. Table 3-1 describes the available states. Several Thread class methods ” Abort , Suspend , Interrupt , and Resume ” control thread execution inside your program. Each of these methods can change the operating state of your thread.

Table 3-1: Thread Operating States

State

Description

AbortRequested

Indicates that the Abort method has been called on the thread

Running

Indicates that the thread has been started and is running

Stopped

Indicates that the thread has terminated because the delegate method completed or has been interrupted by the Interrupt method

Suspended

Indicates that the thread has stopped processing because of the Suspend method but is not terminated

SuspendRequested

Indicates that the Suspend method has been called on the thread

Unstarted

Indicates that the thread has been created but not started

WaitSleepJoin

Indicates that the thread s delegate method has called either Sleep or Wait on a resource or has called Join to wait on another thread

Abort

The Abort method is designed to stop a thread from running in a controllable, well-defined manner. Abort causes a special ThreadAbortException to be immediately raised in the thread delegate method and is the best way to stop a thread dead in its tracks. If your thread delegate does not catch the exception, the thread is automatically stopped and the thread state changes to Stopped . If the delegate catches the exception, the delegate has an opportunity to cancel the abort request by calling the static ResetAbort method of the Thread class within the delegate catch block. When ResetAbort is called, the delegate might continue processing outside the originating try/catch block that caught the exception. The following code fragment shows how to catch the ThreadAbortException in a delegate method. We also placed comments that describe how you can cancel the abort operation using the ResetAbort method.

C#
 publicstaticvoidMyThreadMethod() { try { //Dosomethingusefulhere } catch(ThreadAbortExceptione) { Console.WriteLine("Caughtthreadabortexception: "  +e.Message); //WecouldcallThread.ResetAborthere,andthedelegatewill //continueprocessingafterthistry-catchblock. } } 
Visual Basic .NET
 SharedSubMyThreadMethod() Try Dosomethingusefulhere CatcheAsThreadAbortException Console.WriteLine(Caughtthreadabortexception:  _ +e.Message) WecouldcallThread.ResetAborthere,andthedelegatewill continueprocessingafterthistry-catchblock. EndTry EndSub 

Suspend , Interrupt , and Resume

Another possible way you can stop a thread from running is by calling Suspend or Interrupt . Suspend is different from Abort because with Suspend , your thread delegate method only pauses and does not experience an exception. Calling Suspend will immediately suspend a thread from running and allows you to continue running the thread at a later time by calling Resume .

Calling Interrupt on a thread will also suspend a thread from running, but only if the delegate routine calls the Monitor.Wait , Thread.Sleep , or Thread.Join method during processing. If the delegate routine never calls one of these methods, the thread will not be interrupted. If your thread does get interrupted and you need to get the thread running again, you ll have to call the Resume method.

Finishing a Thread

When your thread starts running after calling Start , it runs by default in the foreground, meaning that your main program will wait for your thread to complete before the main program can exit. This wait is great because your main program will not have to make special code to wait on one or more threads to complete, especially if your threads are doing something critical in your application such as saving important data to a file. If you don t want the main program to wait for your thread to complete, you can change the Thread class property IsBackground from the default Boolean value false to the value true . When the value is true , your thread will run in the background. If the main program exits while your thread is running in the background, the common language runtime will automatically invoke the Abort method on your thread when the main program exits. If your thread is not doing something critical to your application, running a thread in the background is advisable.

If you need to know when a thread has completed or if you want to wait on a thread to complete, you can call Join . Join is designed to wait on a thread until the thread completes, or it can wait for a specified amount of time until the time expires or the thread completes.

There is a simple downloadable thread sample named ThreadSample that demonstrates how to create and run a thread that s designed to update a shared variable named m_SomeNumber in the delegate method. The main program prints the shared variable in one-second intervals 10 times, showing how the shared variable has been changed by the delegate method.

start sidebar
Windows Forms I/O Problem Using Threading

As we have discussed so far in this chapter, we highly recommend using threads to perform blocking operations asynchronously, which enables you to develop applications that provide a much better experience for the user. This recommendation is especially true when working with Windows Forms. Rather than have the form freeze up while you re waiting on a blocking call, everything in the user s screen remains nice and responsive .

When working with threads while developing a Windows Forms application, there s a good chance that you might want to perform I/O on a Windows Form control such as a text box while running a thread delegate method. Doing so presents an I/O problem because the form is designed to process I/O within a Windows Form thread while your delegate method runs in another thread. For a Windows Form to handle I/O correctly, you must allow the Windows Form thread to coordinate (or marshal) the I/O calls. The form processes these calls sequentially, so you should not write directly to the form from another thread.

To allow a Windows Form to coordinate I/O, a special method is available in the forms ( System.Windows.Forms.Form ) object named Control.Invoke that s designed to marshal the I/O calls for a forms thread. Control.Invoke accepts a delegate method as a parameter and will run your delegate method from within the Windows Forms thread to coordinate I/O. The following programming steps can help you successfully use Invoke to coordinate I/O from a thread. This code assumes that the delegate methods are defined within your form class, so we can easily reference form objects using the this object.

  1. Define a method that will write a string message to your form. The following method is designed to send a message to a text box in a Windows Form:

     publicvoidMyTextBoxWriter(stringMessageToWrite) { this.StatusBox.Text=MessageToWrite; this.StatusBox.Show(); } Define a delegate method description for the method in step 1. publicdelegatevoidmyDelegateMethod(stringTextMessage); Declare an instance of your form writer method as a delegate. myDelegateMethodFormWriterMethod=new myDelegateMethod(MyTextBoxWriter); 
  2. Call your forms Control.Invoke method from any thread in your application to marshal the delegate to the forms thread to perform I/O. Because the delegate method is designed to take a string as a parameter, we have to set up an argument array with a string as the first element and pass it to Invoke .

     object[]args=newobject[1]; args[0]= "Thisisatest"; this.Invoke(FormWriterMethod,args); 

    Additionally, there is an asynchronous version of Invoke named BeginInvoke and a results method named EndInvoke that will allow you to perform Invoke using the .NET Framework asynchronous pattern. The .NET Framework asynchronous pattern design is discussed in the Asynchronous Pattern section later in this chapter.

    On the downloadable chapter samples page, we have provided a Windows application named WinNetworkIO that demonstrates how to develop a client network stream application that uses threads and updates a Windows Form with I/O from the network using the form Invoke method.

end sidebar
 

Using Thread Pools

The .NET Framework offers another way to use threading for running short program tasks by using the System.Threading.ThreadPool class. Thread pools are designed to create and maintain a pool of running threads that service delegate methods from a queue. The idea is to conserve the thread creation and deletion process when you are performing many small tasks asynchronously. The ThreadPool class is statically defined and is available to your application without instantiation.

To queue up a task to a thread pool, you have to define a WaitCallback delegate method. A WaitCallback delegate method is similar to the ThreadStart delegate method we described earlier, except that WaitCallback accepts a State parameter that allows you to pass state information to your thread when it s queued to the thread pool. The State parameter allows you to pass any object type, such as a string or even an integer, into your delegate method, which is convenient because you can pass objects that might be needed during the asynchronous operation. The following code fragment demonstrates how to create a WaitCallback delegate method. In the method, we expect an integer type to be passed in the State parameter.

C#

 voidMyThreadPoolMethod(objectState) { //Assumeanintegertypewaspassedtothe //stateobjectparameter. intg=(int)State; //Dosomethingusefulhere } 

Visual Basic .NET

 SharedSubMyThreadPoolMethod(ByValStateAsObject) 


Network Programming for the Microsoft. NET Framework
Network Programming for the MicrosoftВ® .NET Framework (Pro-Developer)
ISBN: 073561959X
EAN: 2147483647
Year: 2003
Pages: 121

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net