Simple Multi-Threading

We’ve all installed programs on our computers and seen different things happening on the screen right?
Progress bars counting to 100%… Labels changing to show us what file is being worked on… Did you ever wonder how to do that? If you like essentially useless, but attractive stuff like that as much as I do, you’re answer is definitely going to be “YES!”

Using an installer as an example, the simple theory behind it is that the installer runs on (at least) two different threads. When working with threads, imagine the execution of the application flows like a river; a single passage of the water from point A to point B.
If the river should split down another tributary, though, some of the water goes in a different direction, doesn’t it? That’s what threads do, it basically allows you to split an application up, so that it can do more things at any given time.

Let’s get into a simple example…

The following code opens a connection to a database and writes the selected data into a ListBox control (not a practical application, as most people won’t go looking through a full table’s worth of data in a ListBox, but it shows you what threading actually does quite nicely on the form).
The data access will be done on a separate thread, while another thread counts from 0 while the data access thread is still busy.
So, we’ll have 3 threads:

  1. The main thread that builds and controls the form
  2. The 2nd thread that queries the database and modifies the ListBox
  3. The 3rd thread that makes the Label count while thread 2 is busy

The form will consist of a Button, a Label, and a ListBox. I’m keeping the names of these controls as the Visual Studio defaults for clarity.

‘ Imports and Global Variables
1 Imports System.Threading
2 Imports System.Data
3 Imports System.Data.SqlClient
5 Private DS As New DataSet
6 Private DA As New SqlDataAdapter
8 Private t1 As Thread
9 Private t2 As Thread
11 Private SetTextDelegate As SetTextCallBack = _
New SetTextCallBack(AddressOf SetTextMethod)
12 Private AddItemDelegate As AddItemCallBack = _
New AddItemCallBack(AddressOf AddItemMethod)
13 Delegate Sub SetTextCallBack(ByVal Text As String)
14 Delegate Sub AddItemCallBack(ByVal Item As String)

We’re creating 2 threads here. Thread t1 will perform the data access and add items to the ListBox, and thread t2 will just update the Label’s text while t1 is working. This is just for a visual indication that things are actually working.

I’ve already covered connections to SQL Data Sources in my blog, and, since it’s a lot of code, I won’t be including it in this post. Just know that even though we’re working with threads here, there’s nothing different that needs to be done to the code that connects to the database and populates the dataset.

Next we’re going to write the button click event handler, this is just going to set the threads up and start them.

1 Private Sub Button1_Click(ByVal Sender As Object, ByVal e As System.EventArgs) Handles Me.Button1.Click
2     t1 = New Thread(New ThreadStart(AddressOf GetData))
3     t2 = New Thread(New ThreadStart(AddressOf ProgressCounter))
4     t1.Start
5     t2.Start
6 End Sub

When we set the threads in lines 2 and 3, we use AddressOf to tell the threads what code they’re supposed to handle. Lines 11 – 14 in the global declarations are used for interacting with form elements, because controls can’t be accessed from a thread that they weren’t created on.

Now, let’s write the code that t1 will use:

1 Private Sub GetData()
2     ‘ SQL Data Access code goes here
3 Dim CurrentRow As String = Nothing
4 For i As Integer = 0 To DS.Tables(0).Rows.Count – 1
5     CurrentRow = DS.Tables(0).Rows(i)(1).ToString
6     Thread.Sleep(400)
7      Me.Invoke(Me.AddItemDelegate, New Object() {CurrentRow})
8 Next i
9 End Sub

Here, we’re simply looping through the DataSet, and, on each, we’re storing the value in the specified cell. We’re then passing that value to the delegate sub which will use it to execute the next procedure to add it to the ListBox.

1 Private Sub AddItemMethod(ByVal Item As String)
2     ListBox1.BeginUpdate()
3     ListBox1.Items.Add(Item)
4     ListBox1.EndUpdate()
5 End Sub

That’s it for the first thread. The main part of the application is now done, now, let’s get to the 2nd thread:

1 Private Sub ProgressCounter()
2     Dim i As Integer = 0
3     While Not t1.IsAlive = False
4         Me.Invoke(Me.SetTextDelegate, New Object() {i.ToString})
5         i = i + 1
6     End While
7 End Sub
9 Public Sub SetTextMethod(ByVal Text As String)
10     Label1.Text = Text
11 End Sub

Very simple, we’re just storing an integer value and passing it to our delegate so that the delegate can write it to the label.

Lastly, know that a typical application on a 32-bit platform has capacity for 250 threads. You generally shouldn’t need to use more than 10 though (if that). Managing a lot of threads does become quite a nightmare, so be very sure about what your application needs to do before you go crazy creating threads. Also, don’t worry about writing code to close your threads. They will close themselves when they complete execution (i.e. when they’re done working).

In my next post, I’ll show you how to make a delegate procedure modify the text value of any control on your form, so watch this space!