Working with Threads


I had been playing computer games before I got into computer programming. When I started to take programming more seriously, this was the question that came to my mind one day when I was playing games: How do the bad guy and the hero move together at the same time? Didn't I have a single-processor PC?

You must forgive me for asking such a na ve question. After all, it was the DOS era and all the programs I had written were simple applications that did not incorporate very complicated programming logic.

Later on I found the answer.

A program can allocate processor time to units in its body. Each unit is then given a portion of the processor time. Even if your computer only has one processor, it can have multiple units that work at the same time. The trick is to slice processor time and give each slice to each processing unit. The smallest unit that can take processor time is called a thread. A program that has multiple threads is referred to as a multithreaded application. Therefore, a computer game is often multithreaded. If you have two threads, you can use one thread to move the hero and another thread to move the bad guy. In the end, both will seem to move together.

Clever, isn't it?

Note

You can write a computer game even using a language that does not support the concept of threads. However, using threads, writing this kind of program will be easier. The project in this chapter therefore uses threads generously.

But what exactly is a thread?

The .NET Framework documentation defines a thread as follows:

Threads are the basic units to which an operating system allocates processor time, and more than one thread can be executing code inside a process. Each thread maintains exception handlers, a scheduling priority, and a set of structures the system uses to save the thread context until it is scheduled. The thread context includes all of the information the thread needs to seamlessly resume execution, including the thread's set of CPU registers and stack, in the address space of the thread's host process.

However, prior to the .NET era, Visual Basic (VB) programmers could not easily write multithreaded programs. Sometimes, they had to switch to C++, and sometimes they could only do it with the help of third-party tools. If you used VB to write games, well, you chose the wrong language.

With the emergence of .NET, gone are the days where multithreaded applications were the domain of C++ programmers. With the .NET Framework, developing multithreaded applications cannot be easier, regardless of the language you use, whether it is VB, C#, or other .NET languages. The class library provides the System.Threading namespace that contains classes that support multithreading. The central class in this namespace is Thread, which represents a thread. This is the topic of the next discussion.

Using the System.Threading.Thread Class

The System.Threading.Thread class has one constructor whose signature is as follows:

 Public Sub New( start As ThreadStart ) 

To instantiate a Thread object, you pass a System.Threading.ThreadStart. ThreadStart is a delegate used to specify the program code executed by the Thread object. ThreadStart has the following signature:

 Public Delegate ThreadStart() 

For example, to assign a method called Run to a Thread, you construct the Thread object using the following statement:

 Dim myThread As Thread = New Thread(New ThreadStart(AddressOf Run)) 

And then, to start the thread, you call its Start method:

 myThread.Start() 

In this case, after you call the Start method, the code in the Run method will execute.

The Start method is an asynchronous method, therefore it returns immediately.

A Thread object can have many possible states. Understanding these states is crucial in working with Thread objects properly. The possible states for a Thread object are members of the System.Threading.ThreadState enumeration. Table 3-1 summarizes them.

Table 3-1: Members of the System.Threading.ThreadState Enumeration

MEMBER

DESCRIPTION

Aborted

The thread is in the Stopped state.

AbortRequested

The ThreadAbort method has been invoked on the thread, but the thread has not yet received the pending System.Threading.ThreadAbortException that will attempt to terminate it.

Background

The thread is being executed as a background thread, as opposed to a foreground thread. This state is controlled by setting the IsBackground property of the Thread class.

Running

The thread has been started, it is not blocked, and there is no pending ThreadAbortException.

Stopped

The thread has stopped.

StopRequested

The thread is being requested to stop. This is for internal use only.

Suspended

The thread has been suspended.

SuspendRequested

The thread is being requested to suspend.

Unstarted

The Thread.Start method has not been invoked on the thread.

WaitSleepJoin

The thread is blocked as a result of a call to Wait, Sleep, or Join methods.

When first created, a thread is in the Unstarted state. After the Start method is called, the thread's state will become Running. You can suspend a thread by calling the Thread class's Suspend method. After the Suspend method is called, the thread will be in the SuspendRequested state. A suspended thread resumes when the Resume method of Thread class is called. When a thread is resumed, its state gets back to the Running state.

The Sleep method is used often. As the name implies, it sends a thread to sleep for a specified period of time. After the time elapses, you do not have to wake up the thread because it will continue running automatically.

You should terminate a Thread object when the application exits. To do this, you use the Abort method. When this method is invoked, the system throws a System.Threading.ThreadAbortException in the thread to abort it. Calling the Abort method does not guarantee that the thread aborts immediately or at all. If a thread does an unbounded amount of computation in the finally block called as part of the abort procedure, for example, the thread will be delayed from being aborted indefinitely. To make sure a thread has aborted, call the Join method after calling Abort.

The Thread class has the IsAlive property that you can use to inquire about the state of a Thread object. If IsAlive property is True, the thread has been started and has not been aborted.

A thread can also run in the background or foreground. A background thread is the same as a foreground thread, except that background threads do not prevent a process from terminating. You make a thread a background thread by setting the Thread class's IsBackground property to True.

Pros and Cons of Using Threads

In a computer game, it is obvious that you need a way to fool the user into thinking that more than one part of the program is being executed simultaneously. In non- game applications, threads are also useful, especially because using threads increases the level of responsiveness of your program.

For example, if your program is doing something time consuming, such as writing to a disk or waiting for a connection to a Web server, your program can still do something else, such as taking user input. This way, your user does not have to spend time waiting for that particular resource-extensive task to finish. In fact, you can create threads with different priorities. For instance, automatic saving to a disk can happen only when the user is not busy typing.

However, threads consume resources. You should be aware of this and not be too generous with using more threads than necessary. In addition, keeping track of many threads is a taxing programming task.

Using the Thread Class in a Ticker Application

To illustrate the use of the Thread class in a multithreaded application, consider Listing 3-1. The example features a form with two Label controls. It uses two Thread objects (thread1 and thread2) to turn the labels into news tickers. The first ticker flashes three news items in the newsItems array of strings. The second ticker features four items in the businessItems array of strings.

Listing 3-1: The News Tickers Example

start example
 Imports System Imports System.Windows.Forms Imports System.Drawing Imports System.IO Imports System.Threading Public Class Form1 : Inherits Form   Private label1, label2 As New Label()   Private newsItems() As String = _     {"Safest Aerobic Machine Launched", _       "First Dog Cloning Is Only Days Away", _       "Reviving the Extinct Tasmanian Tiger"}   Private businessItems() As String = _     {"FirstMeasure Software to Go Nasdaq", _       "MFMF Directors To Meet For The First Time", _       "First Sign of Economic Recovery Finally At Sight", _       "Euro Hits Record Low (Again)"}   Private thread1, thread2 As Thread   Public Sub New()     label1.Width = 280     label1.Height = 30     label1.Location = New Point(1, 10)     label1.TextAlign = ContentAlignment.MiddleRight     label2.Width = 280     label2.Height = 30     label2.Location = New Point(1, 40)     Me.Controls.Add(label1)     Me.Controls.Add(label2)     thread1 = New Thread(New ThreadStart(AddressOf MoveLeft))     thread1.Start()     thread2 = New Thread(New ThreadStart(AddressOf MoveRight))     thread2.Start()   End Sub   Private Sub MoveLeft()     Dim counter As Integer = 0     Dim max As Integer = newsItems.Length     While (True)       ' get news headline       Dim headline As String = newsItems(counter)       counter += 1       If counter = max Then         counter = 0       End If       Dim i As Integer       For i = 0 To headline.Length         label1.Text = headline.Substring(0, i)         Thread.Sleep(60)       Next       Thread.Sleep(100)     End While   End Sub   Private Sub MoveRight()     Dim counter As Integer = 0     Dim max As Integer = businessItems.Length     While (True)       ' get news headline       Dim headline As String = businessItems(counter)       counter += 1       If counter = max Then         counter = 0       End If       Dim i As Integer       For i = 0 To headline.Length         label2.Text = headline.Substring(0, i)         Thread.Sleep(100)       Next       Thread.Sleep(100)     End While   End Sub   Protected Overrides Sub OnClosed(ByVal e As EventArgs)     thread1.Join(0)     thread2.Join(0)     Environment.Exit(0)   End Sub   <STAThread()> Shared Sub Main()     Application.Run(New Form1())   End Sub End Class 
end example

To compile this application, from the directory where the listing-03.01.vb file resides, type the following:

 vbc /t:winexe /r:System.dll,System.Windows.Forms.dll,  System.Drawing.dll listing-03.01.vb 

Figure 3-1 shows what the application looks like when run.

click to expand
Figure 3-1: Headline ticker application

Now, let's explain how this simple multithreaded application works.

First, look at the following part of the class's constructor:

 thread1 = New Thread(New ThreadStart(AddressOf MoveLeft)) thread1.Start() thread2 = New Thread(New ThreadStart(AddressOf MoveRight)) thread2.Start() 

In this example, thread1 and thread2 are instantiated and started. When thread is started, it will begin executing the MoveLeft method. When thread2 is started, the MoveRight method will start running.

The MoveLeft method consists of a While loop that runs indefinitely, until the Thread is aborted:

 While (True)   .   .   . End While 

The MoveLeft method displays the items in newsItems in turn. When the thread first starts, the MoveLeft method gets the first string into headline because counter is 0:

 Dim headline As String = newsItems(counter) 

Then it increments counter so that the next time the code in the While loop is called, the code will get the next string:

 counter += 1 

However, there are only a certain number of strings. So, when the last string in the array is reached, it must go back to the first string:

 If counter = max Then   counter = 0 End If 

Then, for each string, it does a For loop that goes from zero to the length of the string, constructing a substring starting from an empty string. For each loop, it gets a substring that is one character longer than the substring in the previous loop. As a result, the text on the label looks like it is running:

 Dim i As Integer For i = 0 To headline.Length   label1.Text = headline.Substring(0, i)   Thread.Sleep(60) Next 

For each substring to be readable, it must move slowly enough for the reader to read. Therefore, you call the Sleep method of the Thread class and pass 60 to put the thread to sleep for 60 milliseconds. For each string, it will sleep for 100 milliseconds:

 Thread.Sleep(100) 

The MoveRight method is similar to MoveLeft. However, MoveRight works on the business headlines and moves from right to left.

Finally, the two threads terminate when the form is closed. You achieve this by overriding the OnClose method of the Form class. In the OnClose method, you call the Join method of both thread and thread2, causing both threads to be blocked:

 thread1.Join(0) thread2.Join(0) 




Real World. NET Applications
Real-World .NET Applications
ISBN: 1590590821
EAN: 2147483647
Year: 2005
Pages: 82

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