Threading in ASP.NET

Share this article

This tutorial attempts to shed some light on the subject of Threading using ASP.NET. Threading is a technique used to give the user the impression that multiple tasks are executing at the same time. The .NET Framework provides us with many types that allow us to easily and quickly add multi-threading to our .NET Web application. I’ll assume that you have some familiarity with ASP.NET and at least some experience with writing code in Visual Basic.

Through the tutorial we’ll build a pretty simple Web application that should be a good example of some basic threading concepts. Basically we’ll have 2 textboxes in which to enter a phrase, and to enter a time period in which you want the phrase to be rebuilt. Our Web threading application consists of 2 pages, one of which is the page that will start the thread, while the other is a results page that shows us the progress of our thread. The code provided should be very portable and allow you to implement your own threading application very quickly.

Before we get started…

Before we dive into the Web application, let me first give you a quick look at some of the code that you’ll be seeing.

First, we need to import the System.Threading Namespace so we can access the Thread class provided in the .NET Framework. Add this line to the top of your .aspx/.ascx file:

<%@ Import NameSpace="System.Threading" %> 

or in VB.NET:

Imports System.Threading

Now for demonstration purposes, here is a sample long running method. In a real life situation this method would most likely perform a task like processing a Web form, or completing a time-consuming database query.

Public Sub SomeLongMethod() 

       'your code that takes a while to execute

End Sub

Now to execute this method and not leave our Web form hanging, we’ll start a new thread and let SomeLongMethod execute on this new thread. To do this, we have a few options. The technique I’ll use is to set up a new method that will start our new thread running. Here’s a sample thread starter function:

Public Sub SomeLongMethod_Thread() 

'first, declare a new Thread, passing the constructor the address
'of SomeLongMethod. NOTE: SomeLongMethod can be replaced with your
'own method

Dim NewThread As Thread = New _
           Thread(AddressOf SomeLongMethod)

'next we set the priority of our thread to lowest, setting
'the priority to a higher level can cause unexpected results.
NewThread.Priority = ThreadPriority.Lowest

'finally we start the thread executing
NewThread.Start()

End Sub

And that’s it! All we have to do now is replace our call to SomeLongMethod with a call to SomeLongMethod_Thread, and the long method will execute on its own thread. Normally, we would redirect the user to the results page at the end of the SomeLongMethod_Thread method. However in this example I left that out to prevent confusion — I’ll demonstrate it in the following example, which illustrates the use of Threading in an ASP.NET Web application.

Using Threading to Rebuild a String

The first file we’ll look at is default.aspx. This will be the page where we’ll get 2 values from the user, and start a new thread. The second file we’ll look at is the results page, where we’ll poll the Session variables created in the thread, and display current thread stats to our user. I’ll go through default.aspx method-by-method, and then do the same for the results.aspx page. The source code for this file is available at the end of this tutorial.

NOTE: I’ve assumed that your Web server has Session variables enabled. If you have Session variables Disabled, or you have cookies disabled on your browser, the results of the following example will not display correctly.

A Simple Example (default.aspx)

Let’s begin by looking at the Page_Load function for default.aspx

Sub Page_Load(ByVal sender As System.Object, ByVal e   
As System.EventArgs)  
 
SyncLock Session.SyncRoot  
'here, we initialize 3 session variables to hold our results  
Session("Complete") = False  
Session("Status") = ""  
Session("Phrase") = ""  
End SyncLock  
d Sub

In this method we simply initialize 3 session variables that we’ll use in our next few methods. Session("Complete") will be the sentinel for our results page. When the thread is complete we will set Session("Complete") to True. The Session("Phrase") variable will be what we use to hold our partial phrase as we slowly build it. Session("Status") is just a variable to hold the start time and the end time. Now let’s look at our phrase re-building method:

Sub PhraseBuilder()  
 
 
       Dim str As String = ""  
       Dim i As Integer = 0  
 
       Dim startTimeTicks As Long = 0  
       Dim strStartTime As String = ""  
 
       Dim totalSleepTime As Double = 0.0  
 
       'log our start time, in ticks, and in Long Date format  
       startTimeTicks = DateTime.Now.Ticks  
       strStartTime = "Thread Started: " & DateTime.Now  
 
       ' get phrase  
       str = txtPhrase.Text  
 
       'convert users time from seconds to milliseconds  
       totalSleepTime = 1000.0  
       totalSleepTime = totalSleepTime * CInt(txtTotalThreadLife.Text)  
       totalSleepTime = (totalSleepTime / str.Length)  
 
       For i = 0 To str.Length - 1  
 
       'this method will put our thread to sleep for the specified  
       'number of milliseconds. without the sleep, this method would  
       'execute too fast to see the thread working.  
       Thread.Sleep(totalSleepTime)  
 
 'we use synclock to block any other thread from accessing  
 'session variables while we are changing their values.  
       SyncLock Session.SyncRoot  
         
 Session("Status") = "Thread is " & _  
 Format((i / (str.Length - 1)) * 100, "#0.00") & _  
       "% complete." & " - Time Elapsed: " & _  
       Format((DateTime.Now.Ticks - startTimeTicks) / 10000000 _  
       , "#0.00") & " sec. Target: " & txtTotalThreadLife.Text & _  
       ".00 sec."  
 
       End SyncLock  
 
       SyncLock Session.SyncRoot  
 'rebuild phrase 1 letter at a time  
       Session("Phrase") &= str.Chars(i).ToString  
       End SyncLock  
 
 
       Next  
 
 'our method is complete, so set the Session variables  
 'accordingly  
       SyncLock Session.SyncRoot  
       Session("Status") = strStartTime & _  
 "<br>Thread Complete. End Time: " & DateTime.Now & "<br>"  
 
       Session("Complete") = True  
       End SyncLock  
 
   End Sub

Ok, now let’s dissect this method a little. Basically what we’re doing here is forcing a method that would otherwise run quickly to run on a schedule based on user input. This is done using the Thread.Sleep(ByVal millisecond as Integer) method. This method allows us to put the thread to sleep for the specified number of milliseconds. This Sleep method can be used in any method, not just one that’s executing on a new thread.

The other interesting technique we utilize is the use of the Synclock method. Synclock is used to block other threads from trying to obtain the same Synclock. To protect the variables from simultaneous access, we need to obtain the same Synclock before we access the variables everywhere else in the code. This is a necessity in a multi-threaded Web application to ensure that two methods aren’t reading/writing to the same variable at the same time. The Synclock method is identical to using the Monitor.Enter(Me) method that’s also provided in the System.Threading Namespace.

There are only two methods left to go! The next method we’ll look at is the PhraseBuilder_Thread function. This function is almost identical to the example at the beginning of the article:

Sub PhraseBuilder_Thread()   
 
       'method to start our phrase builder method executing  
       'on a new thread.  
 
       Dim myThread As Thread = New Thread(AddressOf PhraseBuilder)  
 
         
       myThread.Priority = ThreadPriority.Lowest  
 
       '//start the new thread  
       myThread.Start()  
 
       'now redirect to the results page  
       Response.Redirect("results.aspx")  
 
End Sub

Now all that’s left is to call our PhraseBuilder_Thread method when the user clicks the submit button. Here’s the short code:

Sub btnSubmit_Click(ByVal sender As System.Object, ByVal e    
As System.EventArgs)  
 
'start PhraseBuilder thread...  
PhraseBuilder_Thread()  
End Sub

A Simple Example(results.aspx)

Now we’ll take a look at the results page. Basically our results page will check the status of the Session("completed") variable on the Page_Load, and react accordingly. Here’s the Page_load function for results.aspx:

Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)   
'Put user code to initialize the page here  
'check the value of Session("Completed"), if it is True, stop writing  
 
If Session("Complete") <> True Then  
 
  'make sure session variables are enabled, if not warn user  
 
  If Session("Complete") <> False Then  
   'error with session variable, Session("Complete") is not  
   'True or False  
   lblComplete.Text = "Error with Session('Complete')"  
 
   Else  
 
'set page to auto refresh page every 2 seconds, until thread is done  
 
   Response.Write("<META HTTP-EQUIV=Refresh CONTENT='2; URL='>")  
 
SyncLock Session.SyncRoot  
   lblStatus.Text = Session("Status") & "<br>Processing. . ."  
   lblPhrase.Text = Session("Phrase")  
   lblComplete.Text = Session("Complete")  
End SyncLock  
 
   End If  
 
 
Else  
'thread is complete, stop writing refresh tag, and display  
'results  
SyncLock Session.SyncRoot  
   lblStatus.Text = Session("Status")  
   lblPhrase.Text = Session("Phrase")  
   lblComplete.Text = Session("Complete")  
End SyncLock  
 
End If  
 
End Sub

This Page_Load function checks the status of the Session("Complete") variable and reacts accordingly. Feel free to customize you results page to suit you needs, but I recommend checking for the case when the Session("Completed") variable is not set to True, or False. This usually happens when Session variables are disabled, or you have cookies disabled. Also, you can remove the Response.Write statement if you don’t want the page to automatically refresh.

In Conclusion

Well that’s all there is to it! Hopefully you have just written your first ASP.NET application using threading.

Using threading in the right places in your applications can greatly improve Web server performance, and also provide you users with a much better Web experience. I hope you enjoyed this tutorial! For more information, visit:

Frequently Asked Questions about Threading in ASP.NET

What is the difference between multi-threading and single-threading in ASP.NET?

In ASP.NET, single-threading refers to the process where a single thread is used to execute all tasks in a specific order. This means that the next task can only start once the previous task has been completed. On the other hand, multi-threading involves the execution of multiple tasks concurrently using more than one thread. This allows for better utilization of CPU resources and improved performance, especially in applications that involve heavy computations or tasks that can be executed in parallel.

How can I implement multi-threading in ASP.NET?

Multi-threading in ASP.NET can be implemented using the System.Threading namespace. This namespace provides classes such as Thread and ThreadPool that can be used to create and manage threads. For instance, you can create a new thread using the Thread class and start it using the Start method. The ThreadPool class, on the other hand, provides a pool of worker threads that can be used to execute tasks in the background.

What are the potential issues with multi-threading in ASP.NET?

While multi-threading can improve performance, it also introduces potential issues such as race conditions, deadlocks, and thread starvation. Race conditions occur when two or more threads access shared data simultaneously, leading to unpredictable results. Deadlocks occur when two or more threads are waiting for each other to release a resource, causing them to wait indefinitely. Thread starvation happens when a thread is perpetually denied access to resources and thus unable to proceed with its execution.

How can I avoid race conditions in multi-threaded ASP.NET applications?

Race conditions can be avoided by using synchronization techniques such as locks, mutexes, and semaphores. These techniques ensure that only one thread can access shared data at a time, thereby preventing race conditions. For instance, you can use the lock keyword in C# to ensure that a block of code is executed by only one thread at a time.

What is the role of the ThreadPool class in multi-threading in ASP.NET?

The ThreadPool class in ASP.NET plays a crucial role in managing the execution of a large number of tasks using a limited number of threads. It maintains a pool of worker threads and assigns them to execute tasks as they become available. This helps to minimize the overhead associated with thread creation and destruction, thereby improving performance.

How can I handle exceptions in multi-threaded ASP.NET applications?

Exceptions in multi-threaded ASP.NET applications can be handled using try-catch blocks. However, it’s important to note that exceptions thrown by a thread cannot be caught by another thread. Therefore, each thread should have its own try-catch block to handle any exceptions that it might throw.

What is the difference between foreground and background threads in ASP.NET?

In ASP.NET, a foreground thread will keep the application running for as long as it’s executing, while a background thread will not. This means that if all foreground threads have completed their execution, the application will terminate, regardless of whether or not background threads have finished executing.

How can I use the Task Parallel Library (TPL) for multi-threading in ASP.NET?

The Task Parallel Library (TPL) is a set of APIs in .NET that provide a higher-level approach to multi-threading. It provides the Task class, which represents a single operation that can be executed asynchronously. The TPL handles all the low-level details of thread management, allowing you to focus on the logic of your application.

What is the role of the async and await keywords in multi-threading in ASP.NET?

The async and await keywords in C# are used to write asynchronous code in a more readable and manageable way. The async keyword is used to declare a method as asynchronous, while the await keyword is used to suspend the execution of the method until a particular task has completed.

How can I test multi-threaded code in ASP.NET?

Testing multi-threaded code in ASP.NET can be challenging due to the non-deterministic nature of threads. However, you can use techniques such as mocking and injecting dependencies to isolate the code under test. Additionally, you can use tools such as Microsoft’s Task Parallel Library for testing asynchronous and multi-threaded code.

Joshua WallerJoshua Waller
View Author

Josh is currently a Senior at Southeast Missouri State University studying Computer Science. He also works for Element74 Website Designs, where he develops Web applications using ASP.NET.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week