Scott Hanselman

A tale of two techniques: The [ThreadStatic] Attribute and System.Web.HttpContext.Current.Items

May 23, '03 Comments [10] Posted in Web Services | ASP.NET
Sponsored By

Today's lesson learned: the [ThreadStatic] attribute is only useful when YOU control the ThreadPool (and the lifecycle of the threads).  It also reminds one to think about variable scope/lifetime especially in the context of ASP.NET. 

An interesting day and I ran into some code that made interesting (and arguably not necessary use of a static member variable).  More and more I feel that static member variables (whether hidden by a property accessor) will bite you in the ass unless they were really meant to be readonly (WORM - write once read many) variables. 

Two interesting things:

1. I noticed this behavior and saw that the fantastically smart Chris Brumme had good things to say about on DevelopMentor's DOTNET List.   It's kind of a DUH moment when you realize it of course it should work that way.   Definitely don't count on the constructors of static fields marked with ThreadStatic.

2. Don't slap a [ThreadStatic] attribute on a static member when you're operating within ASP.NET as chances are you don't control the thread life...you inherit a worker thread.  ThreadStatic gives you thread local storage, not HttpContext local storage!  If you need to store something away to be used later in the same HTTP request, think about my favorite ASP.NET class, the little-known and not-used-enough System.Web.HttpContext.Current.Items (aside: great article by Susan Warren on Context).  In ASP.NET your code is run on a WorkerThread from the 25 or so threads in the default ASP.NET worker thread pool and the variable that you think is "personal private to your thread" is personal private...to you and every other request that this worker thread has been with.  Under load you may well find your variable modified.

To sum up places to stash things in ASP.NET:

Object Description
Application A key/value pair collection of values that is accessible by every user of the application.
Cache The ASP.NET Cache object, which provides programmatic access to the cache. Arguably a fancier face on Application.
Items A key-value pair collection that you can use to pass information between all of the components that participate in the processing of a single HTTP request. This is a great place to put things that need to be shared between ASP.NET pages and components farther downstream that will inherit the current HttpContext.
Session A key/value pair collection of values that are accessible by a single user of the application (When using InProc Session in ASP.NET 1.0 Session is really just a pretty indexed-by-cookie-guid face on the Cache object!)

When using ASP.NET think twice, then think again, before throwing that static (or Shared) keyword in there.  Perhaps there is a scope better suited for what you're trying to do.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Friday, May 23, 2003 3:49:43 PM UTC
This all seems a bit academic. Why on earth would someone go an put [ThreadStatic] on a static varible which they want to share amongst threads anyway???. Don't put [ThreadStatic] on your variable - problem solved (as long as it's readonly).
Duncan Smart
Friday, May 23, 2003 4:27:26 PM UTC
It's not that they'd put [ThreadStatic] on a static variable they want to share...it's that they were using ASP.NET and put the attribute on thinking that they were creating "Request Local Storage" when really they are getting "Shared Worker Thread Local Storage" - which means the variable can be modified by other ASP.NET users.
Friday, May 23, 2003 4:33:22 PM UTC
Did you leave out ViewState?
Carson McComas
Friday, May 23, 2003 8:15:49 PM UTC
Argubly I left out both Cookies AND ViewState, but since they:

Saturday, May 24, 2003 6:47:32 AM UTC
Good points :)
Carson McComas
Monday, May 26, 2003 3:51:39 PM UTC
Well, using HttpContext is fine for components that will only be used in a web environment, but what about "business components" that will be used by both a windows forms client as by a web client? Would System.Runtime.Remoting.Messaging.CallContext(1) be a viable alternative? Why didn't they just derive from a central context object instead of the "every team (meaning system.web, system.runtime.remoting, ...) develops it's own context"? What's the best approach for my above mentioned problem?

Yves Reynhout
Friday, April 23, 2004 7:40:01 PM UTC
The behavior:

"In ASP.NET your code is run on a WorkerThread from the 25 or so threads in the default ASP.NET worker thread pool and the variable that you think is "personal private to your thread" is personal private...to you and every other request that this worker thread has been with. Under load you may well find your variable modified."

Where is this behaviour documented? I found others articles and code, that assume that from BeginRequest to EndRequest, the thread is only one, and that it is not used by another request in the meantime. I know that the same thread is used in different request, but I think that in a serialized way: one request, then another.... Then, from BeginRequest, to EndRequest, the ThreadStatic variable is under our sole control. Or not???

For example:

http://weblogs.asp.net/yreynhout/archive/2003/03/19/4061.aspx

The author trusts that his object lives untouched from BeginRequest to EndRequest.
Wednesday, June 23, 2004 9:22:02 PM UTC
I, like Angel, believe/assume that a single thread processes a request from beginning to end. Based on that assumption, I created my own CallContext class, using the ThreadStatic attribute, to avoid having my business objects and data-access objects depend on system.web.

What I would like to know is the cost of using ThreadStatic. Back in my Windows programming days, thread local storage was a precious commodity. Because of that, I only use the ThreadStatic attribute to store a reference to the current CallContext and I keep other thread-local data within that object.

It's working well so far, but the "Under load you may well find your variable modified" statement above worries me.

Another problem related to object running in web and non-web contexts is configuration. I've had to reimplement a fair amount of System.Configuration in order to implement configuration redirection (specifying a configuration file explicitly). This is especially important when testing my business components using NUnit. Any tips on an elegant solution would be appreciated.
Hugh Lynch
Thursday, November 03, 2005 1:38:15 AM UTC
Unfortunately even if you clean up after yourself in EndRequest, neither CallContext nor ThreadStatic can actually be trusted in ASP.Net due to thread agility *during a request's lifetime*.

I've posted on my blog about this, because it bit me (see link)
Thursday, January 19, 2006 10:41:14 PM UTC
When I firstly come to see this topic, it scares me so much. But after some reading, I thought this concern does not hold for most cases. As far I know, one http request is handled by one worker thread from the thread pool. The only exception is async pages. But that's another story. If you create async pages, you must be very careful about the objects passed to the background method that will be running on another thread. Moreover, in the new thread, HttpContext.Current will not work, it's null, you have to pass the HttpContext object manually. Why? Because in the low leverl, HttpContext.Current is stored in a ThreadStatic variable.

courlake
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.