Developing Long Running Tasks Using Asynchronous Programming with VB.NET 2005

This article gives you a practical understanding of asynchronous programming. It also gives you a simple template for developing asynchronous-based applications in a very easy manner.


A downloadable file for this article is available here.

For this article, I assume that you know enough of the basics for working with VB.NET controls, threading, events and delegates, ADO.NET, and so forth using Visual Basic.NET 2005. The entire source code for this article is available in the form of a downloadable zip. The solution was developed using Microsoft Visual Studio 2005 Professional Edition with Microsoft SQL Server 2005 Developer Edition on Microsoft Windows Server 2003 Enterprise Edition. 

Even though I believe that the source code available with this contribution can work with Microsoft Visual Studio.NET 2003/2002, I didn’t really test it in any other environment. I request that you post in the discussion area if you have any problems with execution.

Methods of presenting long running tasks

When we develop any Windows-based application, we generally deal with the existing VB.net controls. For further flexibility, we may even use a few third party controls/components out of necessity.

When we develop applications which do heavy processing like scientific calculations, backing up/restoring a database, multi-level cross-tab reporting, summarizing data, file/directory search, and so forth, we are required to inform/display the status to the user, so he or she can see what our application is currently doing.. 

If we develop the application without showing the status of the process to the user, it would not be a very pleasing application. In some scenarios, some developers simply use the “Please wait” message. Even though this is better than nothing, it seems to work best for very short processing times (roughly three to four seconds).  Imagine developing an application which processes a task for an hour, and shows a “Please wait” message for that entire time! Certainly, most users would not be pleased.

A better method would be to use a label/text area/list box/list view/status bar to show the latest status of the process. In the case of a label, it can only show the latest status of the process. The best example for this would be a file/directory search. A text area/list box/list view may be used when you want to show each and every status of the process (history) by adding the latest status at the end.  You might want to use this method for CD burning software. A status bar might be used when you are dealing with a process that requires a small number of steps, but it may not be eye catching. One example of this is Yahoo/MSN messenger.  To make the status bar eye catching, these IM programs are equipped with beautiful icons, to let user know the status.

Another method of showing the status of a process would be using a “progress bar.” It is really eye catching for the user. The user would certainly appreciate it, if it is available. Some CD/DVD burning software include all of the above methods together in a synchronized and effective manner (along with text indicating the expected time to finish). 

The ultimate goal for any application developer would be making the user happy, even during long-running applications.

{mospagebreak title=What exactly is the problem?}

Selecting a method for displaying/presenting the status to the user is not a problem at all. The problem always lies with the synchronization of the process and presentation. 

Sometimes we cannot anticipate how much time the process will take to complete. In such scenarios, it is useless to select a method which shows an expected finishing time. We really need to justify the scenario and possibilities to the best of our abilities. Once the selection is made, we need to implement it with a particular approach.

Now we come to the point of our “approach.” Most developers don’t care about the best and most professional approaches for dealing with the issues of long-running tasks. To be frank, any professional approach is a bit difficult to achieve.  But if we make it in the form of a template, it could be reused a number of times.

Let us consider the normal approach for working with a task. I prepared a long-running task as follows:

PrivateSub ExecuteTask()
        For i As Long = 1 To 10000000
            Me.lblMessage.Text = i
        Next
    End Sub

To execute the above task, I wrote the following code in a button click event:

PrivateSub btnNormal_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnNormal.Click
        ExecuteTask()
        MsgBox(“Finished”)
    End Sub

There exists no complication anywhere in any of the above programs. I am showing the status of my task using “lblMessage.” If you execute your application, your application hangs (or doesn’t respond to you) for some time. It only responds when the application has executed the task successfully. Even if you ask it to show you the status, it will not respond in a timely fashion. 

How do we deal with this issue professionally? Go through the next section for a solution.

{mospagebreak title=The solution is here}

In the previous section, I showed you the problem, but didn’t give you a solution. Before giving you the solution, you need to understand the application execution a bit more in depth.

When an application is executed, it starts its own thread of execution (generally called the main/application thread). The application is closed when the thread is ended.  Nevertheless, of any of number of instructions you embed within the application, all are executed in the same thread, one by one, in a hierarchical manner (sometimes even in the form of a top-down approach to execution).

If our application is trying to complete all the instructions in a task, it does so with a hell of a lot of speed. With this speed, it would not even find time to update the screen (even if we ask it to do so)! 

The simplest solution is including a new simple statement as follows:

    Private Sub ExecuteTask()
        For i As Long = 1 To 100000
            Me.lblMessage.Text = i
            Application.DoEvents()
        Next
    End Sub

You can observe the statement “Application.DoEvents.” It really does the magic.  Now, our application responds to the user in a professional manner. What exactly does this new statement do? During the execution of several instructions of a process/task, that simple statement pauses the application thread for a moment to handle user events (like updating the screen, handling mouse events, and so on).

Until now, it is quite perfect. The above method works quite well if you are working with several instructions. Let us consider the possibility that you are working on only one instruction from your application’s point of view. The best example of this is the instruction “check for database connectivity.” It is really a simple instruction to check for the existence of a database. Now, the task’s delay is not from our application. It is from an external source. Our application will not respond, unless the current instruction is completed (even though you use Application.Events). You cannot even close your application properly if it is frozen or unresponsive.

The second method (which I consider as the best) involves using a separate thread for the process and executing the same thread asynchronously with call backs for better presentation. 

{mospagebreak title=The asynchronous way of dealing with a long task}

This is a very complicated method when you compare it with the method given in the previous section.  But it is essential for certain types of applications.

To deal with the asynchronous method, you should be familiar with threading, eventing and delegating. If you are very new to delegating, I request you go through the “events and delegates” information on MSDN before proceeding with the code. The following is the entire code, which you can use as a template for your own long-running applications:

    Dim thExecuteTaskAsync As Thread = Nothing
    Private Sub StartExecuteTaskAsync()
        ‘clear existing thread
        If Not thExecuteTaskAsync Is Nothing Then
            thExecuteTaskAsync.Abort()
            thExecuteTaskAsync.Join()
            thExecuteTaskAsync = Nothing
        End If
        ‘start a new thread to execute the task asynchronously
        thExecuteTaskAsync = New Thread(AddressOf
ExecuteTaskAsync)
        thExecuteTaskAsync.Start()
    End Sub

    Private Sub ExecuteTaskAsync()
        For i As Long = 1 To 100000
            ‘access delegate to show status on GUI
            Invoke(ShowStatus, New Object() {CType(i, String)})
        Next
    End Sub

    ‘================================================================
    ”DELEGATE declaration 
    Private Delegate Sub delShowStatus(ByVal i As String)
    Dim ShowStatus As New delShowStatus(AddressOf ShowMsg)
    Private Sub ShowMsg(ByVal i As String)
        Me.lblMessage.Text = i
    End Sub
    ‘=============================================================
    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e
As System.Windows.Forms.FormClosingEventArgs) Handles
Me.FormClosing
        ‘this is necessary if the form is trying to close, even
before the completion of task
        If Not thExecuteTaskAsync Is Nothing Then
thExecuteTaskAsync.Abort()
    End Sub

    Private Sub btnAsynchronous_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) Handles
btnAsynchronous.Click
        StartExecuteTaskAsync()
        MsgBox(“Finished”)
    End Sub

In this article, I simply wanted to explain the basic topics of asynchronous programming in VB.NET 2005/2003. The sample codes given in this article may not be suitable for every application. You can neither consider it as the best in performance nor the best in programming methodologies. It is up to you to make the correct decision based on the necessities of the applications being developed.

Even though I developed a simple application to test asynchronous programming for long-running-tasks, you can really develop highly complicated applications in an easy manner with the help of asynchronous programming and its features.  In my upcoming articles, I shall add a few more examples covering the same.

If you are new to developing long-running-tasks in ASP.NET web applications, you can refer to another contribution of mine. 

Any feedback, suggestions, bugs, errors, improvements etc., are highly appreciated at jag_chat@yahoo.com.

4 thoughts on “Developing Long Running Tasks Using Asynchronous Programming with VB.NET 2005

  1. hello guys. This contribution introduces you to work with long running tasks. enjoy and give me feedback

  2. I have code in which i need to enable a cancel button during a process.
    Right now i have used Asynchronous programming.But that alone did not work for me.I had to use application.Doevents along with it to work. Pls go through my code and let me know what changes i need to make to work only with Asynchronous programming.Pls

    private void button1_Click(object sender, EventArgs e)
    {
    factorDelegate = new AsyncFactorCaller(Test);
    IAsyncResult result = factorDelegate.BeginInvoke(new AsyncCallback(CallBackTest), null);
    while (!result.IsCompleted)
    {
    EnableCancelButton();
    }
    btnCancel.Visible = false;
    factorDelegate.EndInvoke(result);
    }

    public void EnableCancelButton()
    {
    btnCancel.Visible = true; // making Button Visible
    btnCancel.Refresh(); // Invalidating it
    btnCancel.Focus(); // setting the focus
    Application.DoEvents();
    }

  3. In Vb, you can use the same code that is called when the task starts to make sure it is not already running:

    here’s a button that would cancel the function…

    Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
    ‘clear existing thread
    If Not thExecuteTaskAsync Is Nothing Then
    thExecuteTaskAsync.Abort()
    thExecuteTaskAsync.Join()
    thExecuteTaskAsync = Nothing
    End If
    Beep()

    End Sub

  4. If you don’t add “application.DoEvents” to the ExecuteTask Sub, the system will hang from even if it is running asynchronously (it did on my computer anyway)

    I changed the “ExecuteTask” code to look like this:

    Private Sub ExecuteTask()
    For i As Long = 1 To 100000
    Me.lblMessage.Text = i
    Application.DoEvents()
    Next
    End Sub

[gp-comments width="770" linklove="off" ]