VBNET:Multithreading

From GPWiki
Jump to: navigation, search

Introduction

Multithreading is an insanely powerful tool for developers. Traditionally, it has been limited to use in C/C++. But with the .Net platform, developers can now easily add multithreading to their applications. Multithreading is exactly that, running multiple threads for a single application. This allows many options that just aren't possible to a single threaded application.


Single Threaded

One thread does the work for the entire process. This means that your application will flow in sequential order, from one subroutine to the next. If you have a long subroutine, you will have to wait until it finishes before moving on to the next.


Multithreaded

There are two or more threads for the process. This time, your big subroutine can execute in a seperate thread while the rest of the application does other work (used alot for keeping the form responsive while something intensive is being done).


Why though?

Well, let's stop and think about an application that needs to perform something CPU intensive. In a single threaded environment, the intensive subroutine would execute, forcing the rest of the application to be unresponsive until it finishes. This can be worked around by using Application.DoEvents, but that only alleviates the problem a little. In a multithreaded environment, you could spawn a new thread for the intensive subroutine. This thread continues it's work in the background while the rest of the application is not only responsive, but can perform other tasks. You can also set the priority of the spawned thread to be lower if you wish.


Basic Code

At first thought, this seems extremely difficult to do. But in reality, .Net makes it extremely easy to multithread. That's not to say it's not complex, it can get quite complex later, but the fundamentals are dead simple. For our example, we are going to create a function that fills a listbox with a hundred items: the multiples of 2. This will be our 'intensive' routine that has been talked about.

First, we need the function itself. It is created like any other function:

Public Sub Fill()
  Dim i As Int32
  Dim current As Int32
 
  current = 0
  For i = 0 To 100
    Listbox1.Items.Add(current)
    current +=2
  Next
End Sub

Simple enough. Now, to spawn that function as a new thread, do this:

Dim oThread As System.Threading.Thread
oThread = New Thread(AddressOf Me.Fill)
oThread.Start()

Here we create a new Thread object. AddressOf creates a delegate object of the Fill function (think of it as a type safe, function pointer). We set the thread object equal to the delegate object of Fill, then start the thread. That function will then begin running in its own thread, independant of the calling thread. Neat, eh?

Sleep

Once the thread is running, you have several methods at your disposal to control it. The first is System.Threading.Thread.Sleep. This causes the current thread to 'sleep'. It immediately stops whatever it is doing until it is 'Interrupted'. Threading.Thread.Interrupt wakes a thread back up. Here are some code examples:

Public Sub Fill()
  Dim i As Int32
  Dim current As Int32
 
  current = 2
  For i = 0 To 100
    Listbox1.Items.Add(current)
    current *=2
    System.Threading.Thread.Sleep(3000)
  Next
End Sub

In this example, we have the Fill function force itself to sleep for 3 seconds (3000 milliseconds). Thread.CurrentThread is a public static property for manipulating the currently running thread.


Infinite Sleeping

Dim oThread As System.Threading.Thread
  oThread = New Thread(AddressOf Me.Fill)
  oThread.Start()
  oThread.Sleep(System.Threading.Timeout.Infinite)
 
Dim retValue As MsgBoxResult = MsgBox("Wake Thread?")
  If retValue = MsgBoxResult.Yes Then
    oThread.Interrupt()
  End If

This example forces the newly created thread to sleep (Notice a thread can be forced to sleep either from inside the thread or outside of it) for an infinite amount of time (System.Threading.Timeout.Infinite). It then asks the user if they want to wake it, and if yes, uses the Interrupt() method to do so. If the user would say yes, the thread would resume wherever it left off last.


More

Two more methods that are very similar to Sleep and Interrupt are Suspend and Resume. These use the same syntax, and work in almost the same way. Unlike Sleep, Suspend will stop the thread when it is safe to stop it. Sleep will stop it immediately, even if it is not safe.

Some Extra Tidbits

One common use is for the speed advantages of multithreading in a high performance app (games). DX uses multithreading automatically when doing things such as input and networking. This allows for events to happen asyncronously. Depending on your code layout however, excessive multithreading can harm your framerates. A good rule for games is to have rendering on one thread, input on another, networking on another, and to combine AI, collision detection, and physics into another thread (it can also be on the render thread).

In the top-of-the-line CPUs (P4 w/ HT) and in multiprocessor systems, multithreading can be handled natively. One thread can run on one core, while another thread will run on the other. In most current CPUs, however, multithreading is not handled natively. The single core must split itself between the proccesses on it, therefore each one will run slower than a single thread, however, this can still result in better peformance overall.