A couple of months ago, I ran into a problem where I was seeing a bunch of
ThreadAbortExceptions showing up in my logs. It didn’t take long to track down why – I was calling
Response.Redirect from within a
try/catch block in my ASP.NET code-behind page, and the catch block was catching the
ThreadAbortException and writing it out to the log. But why was the
ThreadAbortException being generated?
It turns out that
Response.Redirect, when called with just the URL, or with the URL and a Boolean value of true, calls the
Response.End then calls
Thread.Abort on the current thread, and, assuming that the page is in a cancellable state, this method throws the
ThreadAbortException, alerting the framework that it is time to stop this thread, and NOW.
Why is this a good thing?
Let’s step back a bit and look at the recommended workaround for dealing with the
ThreadAbortException. Googling for an answer yields many suggestions to call
Response.Redirect and pass the
false as the second, optional parameter. This effectively suppresses the
ThreadAbortException, preventing it from being caught and potentially logged as an error.
All is well and good, right? Isn’t this what we want?
One side-effect of suppressing the
ThreadAbortException is that any code after
Response.Redirect is still executed. This includes any event handlers in the page lifecycle that have yet to execute. This is a waste of system resources, particularly if any of these event handlers contains resource-intensive code.
For the sake of illustration, let’s assume that our page, Default.aspx, has event handlers for most of the Page’s events:
Let’s further assume that each of these event handlers contains a resource- and time-intensive operation. It could be a database call, a network call, or a batch image manipulation. Whatever it is, it requires a lot of resources and a lot of time.
Each event handler will append a string to a
StringBuilder member variable called
_EventsFired. Immediately preceding the call to
_EventsFired will be stored as a Session variable so that the error page, Error.aspx, can display which methods were called. The
Redirect will occur in the
Page_Load method; the user will be sent from Default.aspx to Error.aspx, which will then display the contents of
Here is the code listing of Default.aspx’s Page_Load event handler:
Notice how we are passing
false as the second parameter of
Response.Redirect, meaning that we will prevent a
ThreadAbortException from being thrown.
Here is the output from Error.aspx:
Page_PreInit called. Resource-intensive operation executed. Page_Init called. Resource-intensive operation executed. Page_InitComplete called. Resource-intensive operation executed. Page_PreLoad called. Resource-intensive operation executed. Page_Load called. Resource-intensive operation executed. Page_LoadComplete called. Resource-intensive operation executed. Page_PreRender called. Resource-intensive operation executed. Page_PreRenderComplete called. Resource-intensive operation executed. Page_SaveStateComplete called. Resource-intensive operation executed. Render called.
Page execution did not stop at
Page_Load, even though we told the
Application to complete the request! In all likelihood, this is not the desired behavior, especially if any of the post-
Load event handlers contains time-consuming or resource-intensive operations.
Now let’s modify Default.aspx’s
Page_Load event handler to call
Response.Redirect without suppressing a
ThreadAbortException from being thrown:
Here is the output from Error.aspx (remember, this is the list of events that fired during the processing of Default.aspx):
It’s almost what we want, but notice that we “logged” the
ThreadAbortException. We need a way to exclude that from being logged.
Let’s modify Default.aspx’s
Page_Load event handler one more time:
Here is the output from Error.aspx:
Success! Finally, the desired result!
- The page stopped processing when it was supposed to, right after the call to
- Event handlers later on in the page lifecycle were not unnecessarily executed.
ThreadAbortExceptionwas not “logged” by our catch block. Instead, it was explicitly caught and rethrown up the stack.
So there we have it. Suppressing the
ThreadAbortException can be a bad thing, particularly if there is a lot of resource-intensive code left to execute after the redirect. To prevent those pesky
ThreadAbortExceptions from showing up in our log files, all we have to do is explicitly catch them and rethrow them. On busy sites, this can help to conserve scarce resources.
ThreadAbortException is your friend.
Update 2007-08-01 07:44
Of course, I could have moved the
Response.Redirect outside of the
try/catch. However, the code that prompted the writing of this article is legacy production code that can’t be modified at my whim, hence the need for this approach.
Update 2008-04-05: Moved over from jonsagara.com.Share on: