Chapter 7, “Creating Multiplayer Games,” covered threading briefly when discussing the multiplayer Reversi game. Separate threads were used when looking for the second player across the network when setting up the game and also for the code that polled the open network connection looking for move data during the game.In the first case, a separate thread was necessary because the method that waits for a TCP connection, the TCPListener.AcceptTCPClient method, is a blocking method that halts all code execution as it does its job. Without this code running in a separate thread, the game couldn’t support the ability to cancel the game setup because there would be no way for the program to handle the Click event of a Cancel button if code execution was blocked while running the AcceptTCPClient method.The thread used during the game play wasn’t absolutely necessary, but it makes the game “feel” smoother because the main program thread isn’t handling the polling/waiting for the player across the network to make her turn.If you looked carefully, you might have noticed that two different Visual Basic syntaxes were used when setting up the threads in the two parts of the program. Let’s look at that a bit closer. The following is the thread setup code that polls for in-game player turn data:
oThread = New Thread(New ThreadStart( _
AddressOf CType(Player2, _
NetworkReversiPlayer).LookForTurn))
oThread.Start()
and the following is the code that sets up the thread that looks for the other network player in the game setup phase:
FThread = New Thread(AddressOf LookForIt)
FThread.Start()
It might be difficult to tell because of the extra class boxing code found in the first example, but there are actually two different thread start syntaxes being used here. Broken down a bit, the two syntaxes are as follows:
FThread = New Thread(new ThreadStart(AddressOf <method>))
FThread = New Thread(AddressOf <method>)
Is there a difference in the syntaxes? Actually, Visual Basic .NET users get a little syntactic sugar on their cereal when using threads because the two thread constructors are identical. The ThreadStart class is a required parameter in the constructor of a Thread class; however, if omitted, the compiler adds it automatically.
Passing Parameters to a Thread
Threads can be useful for doing multiple tasks seemingly simultaneously. For example, you might write an adventure game where every computer-controlled element (monster, enemy, and so on) could “think” of its next move in its own thread. Or, your computer-controlled board game opponent might consider each possible move in its own thread.One challenge that comes up when using threads is sending and retrieving information to a routine that’s running in its own thread. You might have noticed that the method used as the parameter of ThreadStart must be a method that takes no parameters and must not return a value. So, how might you send or receive values to code that needs to run in its own thread?One solution involves wrapping the thread code around a class and using class properties as the communication mechanism. The sample project ThreadParametersOne demonstrates such a strategy. This program creates a class that simulates some work and then creates many instances of this class, with each instance performing the work in its own thread. The program then collects the results from each class instance.The class that wraps the thread handling code in the example is called MyThreadExecutor. This class takes three parameters in its constructor: a name parameter (so the classes can be identified from one another) and minimum and maximum integer values. The purpose of the code is to return a random integer that lies between the passed-in minimum and maximum values, but the code that generates the integer needs to run in its own thread. Listing 9-7 shows the MyThreadExecutor class for generating a random number within a separate thread.
Listing 9.7: The MyThreadExecutor Class
Public Class MyThreadExecutor
Private FThread As Thread
Private FMin As Integer
Private FMax As Integer
Private FReturnVal As Integer
Public Event NotifyDone(ByVal sender As Object)
Public Sub New(ByVal n As String, ByVal iMin As Integer, ByVal iMax As Integer)
MyBase.New()
FMin = iMin
FMax = iMax
FThread = New Thread(AddressOf RunMe)
FThread.Name = n
FThread.IsBackground = True
End Sub
ReadOnly Property pThread() As Thread
Get
Return FThread
End Get
End Property
ReadOnly Property pReturnVal() As Integer
Get
Return FReturnVal
End Get
End Property
Private Sub RunMe()
Dim oRand As New Random
'pause a random amount of time to simulate doing actual work
FThread.Sleep(oRand.Next(1, 4) * 1000)
FReturnVal = oRand.Next(FMin, FMax + 1)
RaiseEvent NotifyDone(Me)
End Sub
End Class
The majority of the class is simple setup code, declaring parameters for the elements needed outside the class (the name, the return value, and the thread object itself). The thread is instantiated in the class constructor and is declared to execute the RunMe method when the thread begins.RunMe is where all the “work” occurs. To simulate a method that might actually take a long time (such as looking for a potential move in a two-player game), the thread first pauses a few short seconds and then returns the random value. The RunMe method also raises an event that can be used by the calling program as notification that the work has completed.Listing 9-8 shows the code that creates and runs all of the thread-based classes.Listing 9.8: Creating and Running Thread-Centered Classes
Private Sub cbGo_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cbGo.Click
Dim iNumOf As Integer
Dim iLoop As Integer
Dim FThreads As ArrayList
Dim oExec As MyThreadExecutor
Cursor = Cursors.WaitCursor
lbOut.Items.Clear()
lbStatus.Items.Clear()
Application.DoEvents()
Try
Try
iNumOf = CInt(tbNum.Text)
Catch
iNumOf = 10
End Try
FThreads = New ArrayList
For iLoop = 1 To iNumOf
oExec = New MyThreadExecutor("Thread" & iLoop, iLoop * 2 , iLoop * 3)
AddHandler oExec.NotifyDone, AddressOf DoneNotification
FThreads.Add(oExec)
Next
For Each oExec In FThreads
oExec.pThread.Start()
Next
'wait for each thread to finish
For Each oExec In FThreads
oExec.pThread.Join()
Next
'output the results
For Each oExec In FThreads
lbOut.Items.Add(oExec.pName & _
" generated value " & oExec.pReturnVal)
Next
Finally
Cursor = Cursors.Default
End Try
End Sub
Public Sub DoneNotification(ByVal sender As Object)
lbStatus.Items.Add(CType(sender, _
MyThreadExecutor).pName & " is done")
End Sub
The generator procedure reads a value in a textbox to determine the number of MyThreadExecutor classes to create. Each class instance is generated in a For loop and added to an ArrayList so that it can be accessed later. Note that the individual threads aren’t started in this initial setup loop.Once all the classes have been set up, a second For loop, this one of the For Each variety, launches the thread contained within each MyThreadExecutor class instance. After this, a second loop begins. This one executes the Join method on each thread. The Join method blocks the current process until that thread terminates. By executing each one in a loop, you can guarantee that all of the threads terminate before the code moves beyond this loop.The final loop outputs the results and uses the parameter pReturnVal on each class to find out what random number is generated. You could use either return parameters on the class wrapper or an event handler to send information back to the calling program. Figure 9-1 shows a sample of the program after creating and running 25 class instances.

Figure 9-1: Threaded class example
Try running the example program a few different times using a different number in the textbox each time. The interesting thing is that the program takes roughly three to four seconds to execute whether you’re generating 10 class instances or 100. This is true even though every class instance has a several-second pause built into it. If the same program was written so that each class instance was executed one after the other, then the execution time would increase linearly with the number of objects created. This is a good example of the power of threaded programs.
Understanding Thread Safety
Most of the class definitions in the Visual Studio documentation describe the thread safety of the class. Obviously, having several threads simultaneously attempting to update a listbox or a collection class might produce undefined or undesired results.The example shown that runs the individual classes and returns the results seems to work properly, but I’ve cheated in one place, and that’s in the event callback named DoneNotification. This event simply updates a listbox when one of the threads is about to complete, and the program runs with no errors. However, a bit closer inspection points out that each call of the event handler happens in a separate thread. Because this event handler uses a listbox to update values, and listbox operations (like most Windows forms operations) aren’t considered “thread-safe,” this is a definite no-no.To prove this point, you can add a single line into the notification handler and rerun it:
Public Sub DoneNotification(ByVal sender As Object)
Debug.WriteLineIf(Me.InvokeRequired, "InvokeRequired!")
lbStatus.Items.Add(CType(sender, _
MyThreadExecutor).pName & " is done")
End Sub
The InvokeRequired method returns True if this code is executing in a different thread than the thread in which the control attached to it was created. You should see a number of InvokeRequired! phrases output to the Debug window if you run this code from Visual Studio, which is a warning that you’re trying to access the calling form (and thus the listbox lbStatus) in a different thread from the one that created it.So, does this mean you can’t update any Windows Forms controls when writing multithreaded applications? Well, no, it’s not quite that severe. What’s required, however, is to instruct the program to run the user-interface-specific code back in the main thread, which is the same thread that created all the user interface elements. You do this using a rather complicated syntax with the Invoke method, which is declared on the Control object (thus, all Windows Forms controls contain this method). Listing 9-9 shows how you might use the Invoke method on a listbox. (You can find this code in the folder named ThreadParametersTwo (broken) with the source code that comes with the book.)Listing 9.9: Using Invoke to Run Code Back Up on the Main Program Thread
Public Delegate Sub AddListBoxDelegate(ByVal cThreadName As String)
'thread safe (but doesn't work in this program)
Public Sub DoneNotification(ByVal sender As Object)
Dim oExec As MyThreadExecutor = CType(sender, MyThreadExecutor)
If lbStatus.InvokeRequired Then
Dim oDel As New AddListBoxDelegate(AddressOf Me.AddListBoxValue)
lbStatus.Invoke(oDel, New Object(0) {oExec.pThread.Name})
Else
AddListBoxValue(oExec.pThread.Name)
End If
End Sub
Public Sub AddListBoxValue(ByVal cThreadName As String)
lbStatus.Items.Add(cThreadName & " is done")
End Sub
The Invoke method relies on the declaration of a delegate, which you might recall from Chapter 7, “Creating Multiplayer Games,” is the declaration of a type of procedure or function. Once the delegate type is declared, you can create an instance of it and point it to an actual procedure or function in the program, and then this function can be invoked indirectly by using the Invoke method on any control. When Invoke is called, the code will run on the same thread from which the object attached to the Invoke was created. In this example, the listbox lbStatus is calling Invoke, so the main thread of the program will be where the called function (the function AddListBoxValue in this case) runs.
The second parameter to the Invoke method is an array of objects, which contains any parameters required by the function being invoked. The AddListBoxValue function requires a string parameter, so a single-element object array is created, and the desired string parameter is placed inside it.This is all well and good, but if you run the project in the folder ThreadParametersTwo (broken), you’ll find that the program hangs indefinitely when you try and run it. Can you figure out why it’s hanging? (It took me awhile, let me tell you.)What I’ve managed to do in this example is create a deadlock situation. Two parts of the program are each waiting for the other to execute. When Invoke attempts to run a process in the main thread, it has to wait because the main thread is running Thread.Join. This is a blocking method that’s waiting for the subthread to finish. In other words, the main thread is waiting for the subthread to finish, and the subthread can’t run anything because the main thread is being blocked. Holy deadlock, Batman!The problem lies in calling the Join method on the subthreads. What I’m trying to do is have my cake and eat it, too. I can’t block the main thread from running code until the subthread finishes, but then I expect the main thread to help the subthreads by running Invoked from them. I simply can’t have it both ways. I must instead rework the program to either get rid of the Thread.Joins or get rid of the event handler called from the subthreads that require the use of the listbox and therefore Invoke. The third version of the program, found in the folder ThreadParametersThree, opts to implement the latter solution. It uses the notification and Invoke code to make sure that user interface access is thread-safe, but it doesn’t use Joins to halt the main thread. Instead, as shown in Listing 9-10, it uses the IsAlive method on the thread to find out when it finishes and keeps the loop going until all created threads have completed.Listing 9.10: Waiting for a Thread to Complete Without Using Thread.Join
Dim bDone as Boolean
'wait for each thread to finish. can't use JOIN
bDone = False
Do While Not bDone
For Each oExec In FThreads
bDone = True
If oExec.pThread.IsAlive Then
bDone = False
End If
Application.DoEvents()
Next
Loop
This loop sets the Boolean bDone to True only after all the threads have completed. As long as one remains running, the loop will continue. The Application.DoEvents is necessary so that other parts of the program (in particular, the event notifications) have processing time to execute.Writing multithreaded programs has become a simple process in Visual Basic .NET, and the technique can allow your programs to handle many tasks simultaneously. This can give you an edge when optimizing your games for speed.