Skip to content
cjhaas blog

Basically a place that Chris can post solutions to problems so he can easily find them later

cjhaas blog

Basically a place that Chris can post solutions to problems so he can easily find them later

Creating a simple multi-threaded VB.Net application

Posted on June 25, 2009 By [email protected]

I write lots of desktop tools that help me do my job more efficiently. For instance, I’m normalizing a 98 million row live table and do to the nature of the server, table locks, etc, I’ve decided to pull the data down in batches using a VB.Net app, analyze it and perform a batch update. The high-level code is simple: pull the data, group it and then execute some UPDATE SQL statements. But 98 million rows in a partially non-indexed table takes a looooong time, and maybe its my agency background, but I want some feedback as to what’s going on, and I want more than just a Trace/Console output. At first glance you might just add a progress bar and update it every time you do something, but unfortunately your code loop will block the UI thread from updating until complete (although you can throw some Application.DoEvents in there).

So enter multi-threading. If you think this is overkill then a) you obviously don’t know me and b) I’ve been doing this for years, it only takes a little bit more work to add it. The code below will show you how to create a multi-threaded app with a responsive UI. The code below is all done in Visual Basic 2008 Express Edition.

1) Create a new VB.Net Windows Forms Application.
2) Add a ProgressBar and a Button to the form, leave the names the default names
3) Resize the ProgressBar so you can see more of it
4) Double-click the Button to go into code-behind
5) Add a new class to the project called “Worker” and add the following code to it:


Option Explicit On
Option Strict On

Public Class Worker
    Private _curRun As Integer
    Private _maxRuns As Integer
    Public ReadOnly Property CurRun() As Integer
        Get
            Return Me._curRun
        End Get
    End Property
    Public ReadOnly Property MaxRuns() As Integer
        Get
            Return Me._maxRuns
        End Get
    End Property
    Public Sub New(ByVal maxRuns As Integer)
        Me._maxRuns = maxRuns
    End Sub
    Public Sub Start()
        For Me._curRun = 1 To Me._maxRuns
            System.Threading.Thread.Sleep(250)
        Next
    End Sub
End Class

This code will be doing our primary work. In this case its not actually doing anything except sleeping, you’ll want to replace the System.Threading.Thread.Sleep(250) with your own logic, obviously. We’ll instantiate this code in our primary UI thread later, telling it how many times we want it to loop. I always prefer to pass my required variables in the constructor and make them read-only properties so that I know that they’ll be set. One other thing that you’ll notice, usually in the For loop you’d do something like For I = 1 to Me._maxRuns, creating a local counter variable. Instead, our code creates a class-level variable and gives read-only permission to it so that other code (on another thread) can determine where we’re at. That’s it from the worker class’s point of view. The only bit of thread-related code in here is the Sleep method and that’s for example purposes only.

The next step is to add some logic to the main form’s code-behind to create our worker, create a thread to execute the worker and create a thread to monitor the worker and report back to the form and changes. Before I slap the code up let me explain how it will work. After clicking the button the form’s primary (and only, initially) thread will create an instance of the Worker class. It will then create a new Thread object and tell it that when we execute it it should call the Worker’s Start() method. This alone would give you a responsive UI but unfortunately the Worker thread can’t safely update the UI because they’re on different threads (and VS 2008 will actually throw errors if you try). You can get around this using events or delegates in the worker object but I prefer to make my worker object 100% completely thread un-aware, meaning that it doesn’t report back to anyone, it just does the work and if someone wants to inspect its progress, fine, but it doesn’t really care. So how do we do this? We introduce a third thread whose only job is to check the status of the worker object and tell the main thread to update the progress bar. The code is so simple that I don’t even bother wrapping it in a separate class, I just include a Monitor() method in the form’s code-behind. The code runs a loop until the worker thread signals that its done, sleeping for a bit between each loop so that we don’t flood the UI with update calls (there’s no sense in sending “set the progress bar to 1” a million times a second). Threads automatically handle adjusting their ThreadState so we don’t have to worry about that.

One of the things that takes a little bit of getting used to is the concept that one thread cannot modify controls on another thread. So our Monitor() method can’t actually update the UI. Instead what it does is ask the UI thread to update the UI on behalf of the monitor thread. I know, sounds weird. To do this, our UpdateUI method first calls Me.InvokeRequired which basically asks the form to check if the code calling this method is on the same thread. If its on the same thread it just adjust the progress bar. If its not on the same thread it ask the form to call the same method with the same parameters on its behalf using the Invoke() method. Invoke takes two parameters although the second is optional. The first is a delegate which is basically a type-safe function pointer. If you were using Javascript this would be the same as passing the function’s name without parentheses or just using the function(){} syntax. The second is an array of objects that represent the parameters to pass to the method. The syntax may look confusing but that’s just VB’s way of declaring an array. I think they’re fixing that in VB10 but I’m not sure. We also use a similar concept when the main thread is done to re-enable the button. And that’s it.


Option Explicit On
Option Strict On

Imports System.Threading

Public Class Form1
    Private MonitorThread As Thread
    Private WorkerThread As Thread
    Private W As Worker
    Private Delegate Sub UpdateUIDelegate(ByVal curIndex As Integer)
    Private Delegate Sub WorkerDoneDelegate()

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Button1.Enabled = False                      'Disable the button
        W = New Worker(50)                              'Create our Worker object
        Me.ProgressBar1.Maximum = W.MaxRuns             'Set the maximum value of the progress bar
        WorkerThread = New Thread(AddressOf W.Start)    'Create our Worker thread and tell it that when we start it it should call our Worker's Start() method
        MonitorThread = New Thread(AddressOf Monitor)   'Create our Monitor thread and tell it that when we start it it should call this class's Monitor() method
        WorkerThread.Start()                            'Start the worker thread
        MonitorThread.Start()                           'Start the monitor thread
    End Sub
    Private Sub Monitor()
        Do While WorkerThread.ThreadState <> ThreadState.Stopped    'Loop until the Worker thread (and thus the Worker object's Start() method) is done
            UpdateUI(W.CurRun)                                      'Update the progress bar with the current value
            Thread.Sleep(250)                                       'Sleep the monitor thread otherwise we'll be wasting CPU cycles updating the progress bar a million times a second
        Loop
        WorkerDone()                                                'If we're here, the worker object is done, call a method to do some cleanup
    End Sub
    Private Sub UpdateUI(ByVal curIndex As Integer)
        If Me.InvokeRequired Then                                                           'See if we need to cross threads
            Me.Invoke(New UpdateUIDelegate(AddressOf UpdateUI), New Object() {curIndex})    'If so, have the UI thread call this method for us
        Else
            Me.ProgressBar1.Value = curIndex                                                'Otherwise just update the progress bar
        End If
    End Sub
    Private Sub WorkerDone()
        If Me.InvokeRequired Then                                                           'See if we need to cross threads
            Me.Invoke(New WorkerDoneDelegate(AddressOf WorkerDone))                         'If so, have the UI thread call this method for us
        Else
            Me.Button1.Enabled = True                                                       'Otherwise just update the button
        End If
    End Sub
End Class

One last thing. Debugging a multi-threaded program can be a PITA so one of the things that I do is include a switch to turn it on or off as needed. I prefer to do this with a Compiler Directive by adding #Const MULTI_THREADED = True to the top of my code-behind. Then there’s just a simple change to the button’s click event:


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Button1.Enabled = False                      'Disable the button
        W = New Worker(50)                              'Create our Worker object
#If MULTI_THREADED Then
        Me.ProgressBar1.Maximum = W.MaxRuns             'Set the maximum value of the progress bar
        WorkerThread = New Thread(AddressOf W.Start)    'Create our Worker thread and tell it that when we start it it should call our Worker's Start() method
        MonitorThread = New Thread(AddressOf Monitor)   'Create our Monitor thread and tell it that when we start it it should call this class's Monitor() method
        WorkerThread.Start()                            'Start the worker thread
        MonitorThread.Start()                           'Start the monitor thread
#Else
        W.Start()
        Me.Button1.Enabled = True
#End If
    End Sub

When MULTI_THREADED is False we’ll invoke the worker, blocking the UI thread from ever updating, do the work and reset the button. This can save you a lot of time when debugging. This is also why I like to keep my worker object thread un-aware.

Uncategorized .Net

Post navigation

Previous post
Next post

Comments (11)

  1. giorgiorimmer says:
    October 8, 2009 at 7:33 am

    well expained and layouted out! thanks 🙂

    Reply
  2. Bjarne says:
    December 31, 2009 at 5:44 am

    Just was I was looking for. Very well structures and easy to understand. Gives me a great basis to start working with Multi Threading. Thanks.

    Reply
  3. Haynes Rajwadi says:
    September 18, 2011 at 8:54 am

    I’ve always been confused when it comes to work on Multithreading, I suppose your explanation has helped me a lot…… Superb explanation and nice workout Mate..!!!

    Reply
  4. OXYMOULLY says:
    September 20, 2011 at 5:48 pm

    Man .. Excellent .. Wonderful .. I will bookmark your web site and take the feeds alsoI am glad to seek out a lot of helpful information right here in the submit, we want work out more techniques on this regard, thanks for sharing. . . . . .

    Reply
  5. simon says:
    November 25, 2011 at 11:41 pm

    great explanation, however I would just say that creating a Monitor class is ok for simple applications where it doesn’t need to do much except check status, but if there is a lot of feedback, error handling and updates going on at specific places in the worker thread, then creating and maintaining a monitor thread becomes too cumbersome and not worth the effort. Since you already use “Invoke” anyway whats the big deal in using it from the worker thread? Yes, separation of functionality is good, but in this case it’s not functionality you are separating, it’s logistics, and you may make it more complex to maintain in the long run with many different worker threads doing different stuff.

    Reply
    1. chrishaas says:
      January 6, 2012 at 9:02 am

      This was just a starting example. You can of course make your worker thread aware of your UI logic, that’s up to you. Instead I would recommend giving your worker thread delegates to run at certain intervals and/or milestones, but I’ve definitely done all three. There’s nothing wrong with invoking from your worker thread, its just a personal preference.

      In general, I’m a minimalist when I write methods which, as you say, actually results in more code overall sometimes. To me, a function should do as few things as possible. When I write a function that parses text files, that’s all it does. It has no idea if there’s a UI attached to it. It has no idea if someone is even monitoring it. This way I can optimize and debug that single function without worrying about other code blowing it up. Sometimes, as I did above, I move the variables to a more global state (which still bothers me from an encapsulation standpoint) but only those that I need and I make sure to protect them as ReadOnly and/or copy-on-read.

      Reply
  6. Ervin says:
    January 5, 2012 at 1:38 am

    Nice bro…

    Reply
  7. Yasin says:
    September 10, 2012 at 4:20 am

    Hi,

    I tried running your example as is but could not notice any progress on ProgressBAR. I need to see it working before I can use this any further in my project.

    I am trying to show to progress bar while trying to copy objects from one Map File (MapInfo) to another and want to keep user informed about the progress by using this progressBar.

    Thanks

    Yasin

    Reply
    1. Chris Haas says:
      September 10, 2012 at 8:58 am

      Hi Yasin, I just tried the above code again in VS2012 and it works exactly as expected. It was previously tested in VS2008 and VS2010 and should (to the best of my knowledge) work even in VS2005. The number one problem that people face is that they’re trying to adapt the above code to an existing project which is something that I can’t vouch for (since I didn’t write your code). But from a blank VS project this should correctly multi-thread a progress bar for 12.5 seconds (50 runs times 250ms sleep).

      Reply
  8. Shashi says:
    March 28, 2014 at 9:15 am

    Very useful post and written to understand easily…..
    Thank you

    Reply
  9. Stefan Kruk says:
    December 7, 2014 at 10:10 pm

    You have a very similar approach like the guy that created this Video Tutorial https://www.youtube.com/watch?v=9me7ytACtmM#t=256. Very easy to follow and understand.
    Thank You

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Recent Posts

  • Google open redirect
  • How to use AI to write code
  • Doctrine/Symfony MariaDB DSN connection string
  • Creating a portable copy of pdftotext from source
  • Gravity Forms shortcode getting extra line breaks when used with ACF

Recent Comments

  • jose luis on #2 – VB.Net iTextSharp Tutorial – Add an image to a document
  • Eliezer Castanon on iTextSharp slightly smarter text extraction strategy
  • javad on How to recompress images in a PDF using iTextSharp
  • MANOUS3784 on Flock is awesome
  • Sang on Flock is awesome

Archives

  • June 2026
  • October 2025
  • November 2023
  • September 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • December 2022
  • September 2022
  • April 2022
  • October 2021
  • September 2021
  • April 2021
  • January 2021
  • October 2020
  • August 2020
  • June 2020
  • May 2020
  • December 2019
  • November 2019
  • October 2019
  • July 2019
  • May 2019
  • December 2018
  • October 2018
  • July 2018
  • November 2017
  • October 2017
  • August 2017
  • July 2017
  • June 2017
  • May 2017
  • April 2017
  • March 2017
  • February 2017
  • January 2017
  • September 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • November 2013
  • May 2013
  • April 2013
  • March 2013
  • January 2013
  • November 2012
  • October 2012
  • July 2012
  • March 2012
  • January 2012
  • October 2011
  • September 2011
  • July 2011
  • February 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • June 2010
  • April 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009

Categories

  • Accessibility
  • Advanced Custom Fields
  • Authorize.Net
  • BWP Minify
  • Composer
  • Crappy Google Search Results of the Day
  • CSS
  • Doctrine
  • Drupal
  • Drush
  • Elasticsearch
  • Fun links of the day
  • Google Analytics
  • Gravity Forms
  • HHVM
  • HTML
  • iTextSharp
  • JavaScript
  • Linux
  • mysql
  • nginx
  • Optimization
  • PDF
  • PdfPTable
  • PHP
  • Plugins
  • Ramblings
  • Random things I learned
  • Redis
  • Security
  • simplesamlphp
  • SQL Server
  • SSH
  • SSL/TLS/HTTPS
  • Stack Overflow
  • SVG
  • Symfony
  • Synology
  • Uncategorized
  • Unicode
  • Varnish
  • Vendi Best Practice
  • VIP
  • Weird Google Search Results
  • Windows
  • WordPress
  • WP-CLI

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
©2026 cjhaas blog | WordPress Theme by SuperbThemes