How its done
We'll start off simple and then build on it. Let us begin with a simple operation. We are going to use a loop that counts from 1 to 100 to simulate a long running operation like a file download.
Open a new Windows Forms project and place a Button and a Label on Form1. Double click the Button to bring up its Click event handler then code the event handler as:-
vbnet Code:
'
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For i = 1 To 100
Label1.Text = CStr(i)
Threading.Thread.Sleep(200)
Next
End Sub
Execute the program and click the button. You should notice that the UI freezes until the count is actually complete. Notice that while its frozen, the label is not being updated. Why is this ? Well remember the message loop I mentioned in the Backround section of this article ?. Pressing the button executes the count loop so while this loop is running the message loop of the UI cannot execute and process the repaint messages for the label to update itself. The UI also cannot process mouse clicks, and key presses. You cannot even move the window.
Our solution is to pass off the count to another thread to execute so that the UI's message loop remains unhindered. Remember, the UI's message loop is running in its own thread usually called the main thread or the UI thread.
We'll start off by putting our counting operation into a separate sup:-
vbnet Code:
'
Private Sub Count(ByVal Max As Integer)
For i = 1 To Max
Label1.Text = CStr(i)
Threading.Thread.Sleep(200)
Next
End Sub
What we do next is really a matter of preference. Some people elect to use the BackgroundWorker component to do their multi-threading. I'm not going to show that approach. The forum is rich with examples on using the BackgroundWorker and I've always preferred to do my threading directly. This is the direction I'm going to demonstrate.
Now that we have our count in its own sub. Code the Button's Click event as:-
vbnet Code:
'
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t1 As New Threading.Thread(AddressOf Count)
t1.Start(100)
End Sub
The first line instantiates a new Thread object and passes it the address of the Count sub since this is the sub we want to execute on another thread. Its very important to note that here lies a very serious limitation. Only a sub with a single parameter is allowed. Luckily in our example Count only has one parameter, the number at which the count should end.
The second line starts the thread. Start takes one parameter which it uses to call the sub hence:-
Is like:-
Only it runs on a different thread from the thread it was called from.
If you have Option Strict On the add this Count overload:-
vbnet Code:
'
Private Sub Count(ByVal Max As Object)
If TypeOf Max Is Integer Then
Count(CInt(Max))
End If
End Sub
The reason is because the Thread class's constructor expects the address to a sub that takes one parameter of type object. Option Strict would not allow the address of the Count that takes an Integer as its parameter to be used. Now execute the program.
At this point you should be ready to pull out your hair because you would get this error:-

So just what in God's name is happening here ? Don't worry, that error is actually happening to prevent potential disasters. Remember I said that the UI has its own thread ? Well think about it what is the UI ? Its basically every thing to do with what you see. Labels, pictures, text are all UI elements so any control that is responsible for showing these things is a part of the UI. The windows message loop runs in the UI thread and its responsible for dealing with these controls. Its repainting, its responses to key presses and mouse clicks are all the UI's thread is responsible for. Its rather intrusive for another thread to just come out of nowhere and make a change to a control that its not responsible for. How does this foreign thread know what the UI is doing with any give control at any given moment ? It doesn't and if it interferes with a control while the UI was in the middle of making some change to the control then it can really cause some instability in the application if the control ends up in some invalid state. This is why VB.Net prevents us from changing the Label's text from another thread.
The correct way to go about this is for the foreign thread to politely ask the UI's thread to update the Label for us. If the UI's thread is busy when the foreign thread makes the request, the request is simply queued and when the UI's thread is finish whatever its doing, its message loop will move on to process the other messages in the queue which would eventually lead it to the request to execute the code that would change the Label's text.
Start by making a function to change the Label's text:-
vbnet Code:
'
Private Sub SetLabelText(ByVal text As String)
If Label1.InvokeRequired Then
Label1.Invoke(New Action(Of String)(AddressOf SetLabelText), text)
Else
Label1.Text = text
End If
End Sub
In this sub we call the Label's InvokeRequired property to determine if the Label is being accessed in the current sub from the UI thread or another thread. It returns True if it was called from a foreign thread.
vbnet Code:
Label1.Invoke(New Action(Of String)(AddressOf SetLabelText), text)
The above line submits a request to the UI thread to re-run SetLabelText but instead of running it on the foreign thread, it would then execute on UI thread. InvokeRequired would then return False which would execute the Label's text change. This is the key because the UI thread would only execute the sub when its not busy processing some other message.
All that's left now is to change Count:-
vbnet Code:
'
Private Sub Count(ByVal Max As Integer)
For i = 1 To Max
SetLabelText(CStr(i))
Threading.Thread.Sleep(200)
Next
End Sub
We use SetLabelText to change the Label's text instead of trying to change it directly and viola! We have successfully created a simple multi-threaded application.
One thing remains. Execute the program, click the button and close the form before the count is finished. If you did this in the IDE, you would notice that the program doesn't actually stop. That's because the thread is a foreground thread. Usually, we want a background thread for operations like these. Background threads are terminated when the application terminates which is actually when all foreground threads terminate. The UI thread is a foreground thread and by closing the form we terminate that thread but the application won't terminate because the counter is running on another foreground thread. In this case its undesirable so we must make a change:-
vbnet Code:
'
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t1 As New Threading.Thread(AddressOf Count)
t1.IsBackground = True
t1.Start(100)
End Sub
We set IsBackground to True when we create the thread in the Button's Click event handler. Now, the application has only one foreground thread so the application ends when this thread terminates which automatically terminates all background threads.
This is the most basic way to do straight threading in VB.Net. Now there are a couple things I still want to cover like thread pool threads, using threads to create asynchronous methods on classes, and using events to signal when operations on other threads are finished. I'll cover that on a later date in the next post. Until then folks, happy threading !! 
Note: The following attachment is the demo project. It was written using VS2008 so you must have VS2008 and later to open it.