Visual Basic 2005 for Programmers (2nd Edition)

15.4. Creating and Executing Threads

Figure 15.3 shows how to construct Thread objects and demonstrates class Thread's Shared method Sleep. The program creates three threads of execution, each with the default priority Normal. Each thread displays a message indicating that it is going to sleep for a random interval of from 0 to 5000 milliseconds, then goes to sleep. When each thread awakens, it displays its name, indicates that it is done sleeping, terminates and enters the Stopped state. You will see that method Main (i.e., the Main thread of execution) terminates before the application terminates. The program consists of method Main (lines 633), which creates the three threads, and class MessagePrinter (lines 3659), which defines a Print method containing the actions each thread will perform.

Figure 15.3. Threads sleeping and printing.

1 ' Fig. 15.3: ThreadTester.vb 2 ' Multiple threads printing at different intervals. 3 Imports System.Threading 4 5 Module ThreadTester 6 Sub Main() 7 ' Create and name each thread. Use MessagePrinter's 8 ' Print method as argument to ThreadStart delegate. 9 Dim printer1 As New MessagePrinter 10 Dim thread1 As New Thread(New ThreadStart( _ 11 AddressOf printer1.Print)) 12 thread1.Name = "thread1" 13 14 Dim printer2 As New MessagePrinter 15 Dim thread2 As New Thread(New ThreadStart( _ 16 AddressOf printer2.Print)) 17 thread2.Name = "thread2" 18 19 Dim printer3 As New MessagePrinter 20 Dim thread3 As New Thread(New ThreadStart( _ 21 AddressOf printer3.Print)) 22 thread3.Name = "thread3" 23 24 Console.WriteLine("Starting threads in Main") 25 26 ' call each thread's Start method to place each 27 ' thread in Running state 28 thread1.Start() 29 thread2.Start() 30 thread3.Start() 31 32 Console.WriteLine("Threads started, Main ends" & vbCrLf) 33 End Sub ' Main 34 35 ' Print method of this class used to control threads 36 Class MessagePrinter 37 Private sleepTime As Integer 38 Private Shared random As New Random 39 40 ' constructor to initialize a MessagePrinter object 41 Sub New() 42 ' pick random sleep time between 0 and 5 seconds 43 sleepTime = random.Next(5001) 44 End Sub ' New 45 46 ' method Print controls thread that prints messages 47 Public Sub Print() 48 ' obtain reference to currently executing thread 49 Dim current As Thread = Thread.CurrentThread 50 51 ' indicate that thread is going to sleep 52 Console.WriteLine(current.Name & " going to sleep for " & _ 53 sleepTime & " milliseconds") 54 Thread.Sleep(sleepTime) ' sleep for sleepTime milliseconds 55 56 ' display the thread's name 57 Console.WriteLine(current.Name + " done sleeping") 58 End Sub ' Print 59 End Class ' MessagePrinter 60 End Module ' ThreadTester

Starting threads in Main thread1 going to sleep for 1603 milliseconds thread2 going to sleep for 2355 milliseconds thread3 going to sleep for 285 milliseconds Threads started, Main ends thread3 done sleeping thread1 done sleeping thread2 done sleeping

Starting threads in Main thread1 going to sleep for 4245 milliseconds thread2 going to sleep for 1466 milliseconds Threads started, Main ends thread3 going to sleep for 1929 milliseconds thread2 done sleeping thread3 done sleeping thread1 done sleeping

Objects of class MessagePrinter control the life cycle of each of the three threads created in class THReadTester's Main method. Class MessagePrinter consists of instance variable sleepTime (line 37), Shared variable random (line 38), a constructor (lines 4144) and a Print method (lines 4758). Variable sleepTime stores a random integer value chosen when a new MessagePrinter object's constructor is called. Each thread controlled by a MessagePrinter object sleeps for the amount of time specified by the corresponding MessagePrinter object's sleepTime.

The MessagePrinter constructor (lines 4144) initializes sleepTime to a random number of milliseconds from 0 up to, but not including, 5001 (i.e., from 0 to 5000).

Method Print begins by obtaining a reference to the currently executing thread (line 49) via class THRead's Shared property CurrentThread. The currently executing thread is the one that invoked method Print. Next, lines 5253 display a message indicating the name of the currently executing thread and stating that the thread is going to sleep for a certain number of milliseconds. Note that line 52 uses the currently executing thread's Name property to obtain the thread's name (set in method Main when each thread is created). Line 54 invokes Shared Thread method Sleep to place the thread in the WaitSleepJoin state. At this point, the thread loses the processor, and the system allows another thread to execute if one is ready to run. When the thread awakens, it re-enters the Running state and waits to be assigned a processor by the thread scheduler. When the MessagePrinter object enters the Running state again, line 57 outputs the thread's name in a message indicating that the thread is done sleeping, and method Print terminates.

Module ThreadTester's Main method (lines 633) creates three MessagePrinter objects (lines 9, 14 and 19). Lines 1011, 1516 and 2021 create and initialize three Thread objects. Each Thread's constructor receives as an argument a ThreadStart delegate. A THReadStart delegate represents a method with no arguments and no return value that specifies the actions a thread will perform. Lines 1011 initialize the ThreadStart delegate for tHRead1 with printer1's Print method. The AddressOf operator creates a delegate that references a specific methodin this case printer1.Print. When thread1 enters the Running state for the first time, thread1 will invoke printer1's Print method to perform the tasks specified in method Print's body. Thus, thread1 will print its name, display the amount of time for which it will go to sleep, sleep for that amount of time, wake up and display a message indicating that the thread is done sleeping. At that point, method Print will terminate. A thread completes its task when the method specified by its ThreadStart delegate terminates, at which point the thread enters the Stopped state. When thread2 and thread3 enter the Running state for the first time, they invoke the Print methods of printer2 and printer3, respectively. Threads thread2 and thread3 perform the same tasks as thread1 by executing the Print methods of the objects to which printer2 and printer3 refer (each of which has its own randomly chosen sleep time). Lines 12, 17 and 22 set each Thread's Name property, which we use for output purposes.

Error-Prevention Tip 15.1

Naming threads helps in the debugging of a multithreaded program. Visual Studio .NET's debugger provides a Threads window that displays the name of each thread and enables you to view the execution of any thread in the program.

Lines 2830 invoke each Thread's Start method to place the threads in the Running state. Method Start returns immediately from each invocation, then line 32 outputs a message indicating that the threads were started, and the Main tHRead of execution terminates. The program itself does not terminate, however, because there are still non-background threads that are alive (i.e., the threads are Running and have not yet reached the Stopped state). The program will not terminate until its last non-background thread dies. When the system assigns a processor to a thread, the thread enters the Running state and calls the method specified by the thread's THReadStart delegate. In this program, each thread invokes method Print of the appropriate MessagePrinter object to perform the tasks discussed previously.

Note that the sample outputs for this program show each thread and the thread's sleep time in milliseconds as the thread goes to sleep. The thread with the shortest sleep time normally awakens first, then indicates that it is done sleeping and terminates. In Section 15.8, we discuss multithreading issues that could prevent the thread with the shortest sleep time from awakening first. Notice in the second sample output that thread1 and thread2 were able to report their sleep times before Main could output its final message. This means that the main thread's quantum ended before it could finish executing Main, and thread1 and tHRead2 each got a chance to execute.

Категории