Você está na página 1de 7

Executing Long-Running Tasks with the

Progress Bar in ASP.NET


(Page 1 of 5 )

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.

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).
Executing Long-Running Tasks with the
Progress Bar in ASP.NET - The structure of
the VS.NET solution
(Page 2 of 5 )

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.”

Executing Long-Running Tasks with the


Progress Bar in ASP.NET - The long-running
process (or task)
(Page 3 of 5 )

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).

Executing Long-Running Tasks with the


Progress Bar in ASP.NET - Showing the
progress
(Page 4 of 5 )
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”.
Executing Long-Running Tasks with the
Progress Bar in ASP.NET - What is
“ProcessingStatus.vb” in detail?
(Page 5 of 5 )

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.

Você também pode gostar