Executing Long-Running Tasks with the Progress Bar in ASP.NET

This article demonstrates the use of multi-threading in ASP.NET web applications to execute long running tasks. It also shows the status of that long running task with the help of a progress bar.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 69
August 31, 2005
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

A downloadable file for this article is available here.

The problem

Every scenario in IT, at some level, needs a long running task (process) to be performed.  Long running tasks may also include taking a huge database backup, installing software, formatting a disk, and so on. When we perform any of those tasks, we will watch its progress. 

When a lengthy task starts, some applications get developed with a simple message such as “Please wait” or “Wait a moment” or something similar. Some applications show the progress of the same type of task (sometimes in the form of “percentage complete” or “time remaining”). Showing the progress of a task is pretty common during installations of certain software (or even small packages).

Any user would generally ask (or suggest) that we show the progress of achievement when the system starts processing a lengthy task.  If it is a small task (say, going to complete within one minute), it is reasonable to provide messages such as “Please Wait…” or “Wait a moment.”  But, for long running tasks, it is the programmer’s responsibility to show the progress of achievement to the user visually (before the user complains for it).

It is fairly easy to show the progress of achievement (for long running tasks) on desktop (non–browser based) applications.  But, it is a bit difficult for browser based applications, because we will not be able to maintain a dedicated HTTP request.  Once the browser receives the information (response) from server, the HTTP connection gets disconnected automatically.  If the server takes too much  time to serve for the browser (or client) through HTTP, we may even experience a “Timeout” error.

This article concentrates on working with long running tasks in ASP.NET effectively without consuming too many resources from the server (using multi-threading), and also showing the progress of the task to the user (by auto-refreshing the page every two seconds).

The structure of the VS.NET solution

The entire VS.NET solution can be freely downloaded from this site to reuse the code in your applications. 

Now, what does my VS.NET solution contain?  It simply contains three web forms and one class file as follows:

  • StartPage.aspx (web form)
  • UnderProcess.aspx (web form)
  • Finished.aspx (web form)
  • ProcessingStatus.vb (Class file)

“StartPage.aspx” contains only a button to start a dummy long running process.  The control gets transferred to “UnderProcess.aspx” after the process starts. The “UnderProcess.aspx” shows the progress of achievement in the form of “Percentage complete.”  Once the process completes, the control is transferred to “Finished.aspx”, which simply shows the message “Completed Successfully” using a label.

Even though I did not mention “ProcessingStatus.vb” in the above paragraph, it works behind the scenes.  It gets involved with both “StartPage.aspx” and “UnderProcess.aspx.”  We create a separate thread (for our process) in the “StartPage.aspx” and store information (or status) about the thread using “ProcessingStatus.vb.”  This “ProcessingStatus.vb” supplies the information to “UnderProcess.aspx” (whenever requested).

As we start a separate thread at the server, it remains in memory and continuously works with our process (long running task) without having any relationship with “StartPage.aspx” any more.  We update the status of thread using “ProcessingStatus.vb” at every milestone of achievement.  “UnderProcess.aspx” always gets the information (or status) of the thread from “ProcessingStatus.vb” and displays it visually. Once it receives the information about the completion of thread (indirectly the process), it immediately jumps to “Finished.aspx.”

Starting the Thread

As explained in the previous section, “StartPage.aspx” starts a new thread for a long running process (a dummy task in this scenario).  The first important issue is that we need to import “System.Threading” to “StartPage.aspx” because we deal with threads.

I need to provide a unique id for the process, which is going to start within the thread’s context.  It should be globally unique, because of the potential issues caused by simultaneous access to the same page by more than one user.  I declare and initialize a variable (or object) to hold a globally unique id as follows:

' declaring the Guid
Dim RequestId As Guid
' Create a new request id
RequestId = Guid.NewGuid()

The next step is that we need to start a new thread as follows:

' Create and start a worker thread, to process "something"
Dim ts As New ThreadStart (AddressOf doProcess)
Dim ProcessingThread As New Thread(ts)
ProcessingThread.Start()

What is the funny “doProcess” in the first statement above? It is nothing but our sub-program, which starts our long running process! I separated my entire long process to be under a single sub-program for my convenience (just to attach to the thread). 

Response.Redirect("UnderProcess.aspx?RequestId=" + RequestId.ToString())

The above statement redirects to “UnderProcess.aspx” by carrying the globally unique id we created above.  This statement gets executed immediately after the “ProcessingThread.Start()” statement.

The beauty of the thread is that it would never stop (or interrupt) the flow of the execution of the program, even if it doesn’t complete the process.  It hangs on in memory to complete the process independently by itself, without disturbing the flow of the execution. 

All of the above happens when the user simply clicks on the button provided in “StartPage.aspx.”

The long-running process (or task)

For the long running process, I could not find a proper example other than providing a loop with a certain amount of delay. The loop is iterated ten times and is delayed for about three seconds on every iteration.  In that way, I made a dummy long-running process, which takes about thirty seconds to complete.

I don’t think that the long-running process is the main issue here in this article.  The most important issue is to update the thread information using “ProcessingStatus.vb” and to show it visually with “UnderProcess.aspx.”  The following is the code fragment which is used as the long-running task in this article:

'This method is executed by the processing thread.
Private Sub doProcess()
 
'do some long task processing here
  ProcessingStatus.add(RequestId, 0)
    'start with percentage 0
 
Dim i As Integer
 
For i = 1 To 10
   
Thread.Sleep(New TimeSpan(0, 0, 0, 3,0))
     'wait for 3 seconds for every iteration
    ProcessingStatus.update(RequestId, i * 10)
     'update percentage with a value between 0 to 100
  
Next
 
ProcessingStatus.update(RequestId, -1)
   'after completing the task succesfully, just update status as -1
End Sub

I provided commenting everywhere necessary. The first important issue to understand is that the above sub-program (doProcess) gets executed in a separate thread (as explained in the previous section).  Let us consider the first statement within the above fragment:

ProcessingStatus.add(RequestId, 0)

The above statement adds new GUID with percentage completion as zero using “ProcessingStatus.vb.”  This gets executed when a new process is about to start (taking into the scenario of multiple users).

Thread.Sleep(New TimeSpan(0, 0, 0, 3, 0))

The above statement creates a delay of three seconds.

ProcessingStatus.update(RequestId, i * 10)

The above statement updates the status of GUID added above with a certain percentage value for every iteration of the loop.

ProcessingStatus.update(RequestId, -1)

The above statement updates the percentage of same GUID added above with a percentage value of “-1”.  I made a rule in my application that, if the percentage value is “-1”, the process has been successfully completed (this would in turn make “UnderProcess.vb” understand easily that the process has been completed).

Showing the progress

This is the actual section which shows the progress to the user in the form of a progress bar.  The “UnderProcess.vb” takes care of this process. 

The “UnderProcess.vb” mainly contains two labels, “lblMsg” and “lblProgressBar.”  “lblMsg” shows what percentage it has completed in the form of words.  “lblProgressBar” was designed with a blue background color.  This gets expanded (indirectly; the width of the label) based on the percentage value for every auto-refresh of the same page.  The following is the entire code present in the “page load“ event of “UnderProcess.vb.”

' Get the request id
Dim requestId As New Guid(Request.
QueryString("RequestId").ToString())
'Check the processing thread status collection
If (ProcessingStatus.Contains(requestId))Then
 
'StatusValue contains either a value between 0 to 100 or -1 (-1 shows that the task is finished)
 
Dim StatusValue As Integer =
CType(ProcessingStatus.getValue(requestId), Integer)
 
If StatusValue = -1 Then
  'processing task succesfully finished
   
ProcessingStatus.remove(requestId)
    'remove the status
   
Response.Redirect("finshed.aspx")
    'go to result page
 
EndIf

  'if not finished, display the status
 
' Remove the status from the collection
 
Me.lblMsg.Text = "Wait for a moment.." & StatusValue & "% Completed.."
 
Me.lblProgressBar.Width = Unit.Pixel(2 * StatusValue)
  '200 pixels for 100%
EndIf

' The processing  has not yet finished
' Add a refresh header, to refresh the page in 2 seconds.
Response.AddHeader("Refresh", "2")

I commented the above program very clearly to help you understand every statement in detail. I hope you can understand it.

The “Finshed.aspx” has nothing other than a simple label with a message “Completed Successfully.”  No processing is done at “Finished.aspx”.

What is “ProcessingStatus.vb” in detail?

You must have observed that I wrote plenty of statements starting with “ProcessingStatus” in the above fragments of code (of the previous sections).  Actually, it is my own class having only “shared” members. I developed this class to hold all GUIDs and their percentage values respectively. Of course, you could also develop a very good concrete class rather than a class with all “shared” members.  But, this class has already passed my requirements (apart from thread safety issues).  Let’s see the class:

Imports System.Collections

Public Class ProcessingStatus

  Private Shared Status As New Hashtable

  Public Shared Function getValue(ByVal itemId As Guid) As Object
   
Return Status(itemId)
 
End Function

  Public Shared Sub add(ByVal ItemId As Guid, ByVal oStatus As Object)
   
'make sure that oStatus contains only the values 0 through 100 or -1
   
Status(ItemId) = oStatus
 
End Sub

  Public Shared Sub update(ByVal ItemId As Guid, ByVal oStatus As Object)
   
'make sure that oStatus contains only the values 0 through 100 or -1
   
Status(ItemId) = oStatus
 
End Sub

  Public Shared Sub remove(ByVal ItemId As Guid)
   
Status.Remove(ItemId)
 
End Sub

  Public Shared Function Contains(ByVal ItemId As Guid) As Boolean
   
Return Status.Contains(ItemId)
 
End Function

End Class

In the above program, I maintained a single “HashTable” throughout the class to hold all GUIDs and their respective percentage values. All of the methods in the above class provide a better interface to the “HashTable,” so that you can do all CRUD operations with “HashTable” efficiently.

Summary

The sample downloadable solution is entirely developed using Visual Studio.NET 2003 Enterprise Artchitect on Windows Server 2003 Standard Edition.  But, I am confident that it would work with other versions of Windows (which support .NET 1.1) versions as well.

You can further enhance the solution to show the status (or progress) in a small separate window as well to make it convenient to the user.  But this article focussed only on the core issue of handling tasks rather than UI. I hope this article will help you to solve your needs.

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

blog comments powered by Disqus
VISUAL BASIC.NET ARTICLES

- Basic Form Properties and Modality in VB.NET
- Multiple Document Interfaces in Visual Basic
- Visual Basic for Beginners
- ASP.NET Image to PDF with VB.Net
- MySQL in ASP.NET: Mono using VB.NET
- AsyncFileUpload File Type and File Size Vali...
- Visual Studio: Adding Functionality and Style
- Clocks and Countdowns
- User-defined Functions using Visual Basic Ap...
- Understanding Object Binding in VBA
- Mastering the Message Box
- Testing a Windows Forms Application
- Using Visual Basic.NET Features to Code a Wi...
- Correcting Code in a Windows Forms Applicati...
- Write Readable Code and Comments for Windows...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 6 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials